framefields 2.4.1 → 2.4.2

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 (43) hide show
  1. package/README.md +11 -33
  2. package/dist/audio/index.d.mts +1 -1
  3. package/dist/audio/index.mjs +1 -1
  4. package/dist/{audio-CPApYcWF.mjs → audio-Bb31xJuX.mjs} +2 -2
  5. package/dist/{audio-CPApYcWF.mjs.map → audio-Bb31xJuX.mjs.map} +1 -1
  6. package/dist/{dist-CrK3rmyN.mjs → dist-CxGfrigI.mjs} +43 -3
  7. package/dist/{dist-CrK3rmyN.mjs.map → dist-CxGfrigI.mjs.map} +1 -1
  8. package/dist/effects/index.mjs +1 -1
  9. package/dist/{effects-DxylPSuI.mjs → effects-BOk_2kzM.mjs} +2 -2
  10. package/dist/{effects-DxylPSuI.mjs.map → effects-BOk_2kzM.mjs.map} +1 -1
  11. package/dist/{index-C0o3lLyA.d.mts → index-B3-gwjK6.d.mts} +14 -2
  12. package/dist/index-B3-gwjK6.d.mts.map +1 -0
  13. package/dist/{index-BxARm93R.d.mts → index-BTPLbjt4.d.mts} +40 -11
  14. package/dist/index-BTPLbjt4.d.mts.map +1 -0
  15. package/dist/index.d.mts +7 -3
  16. package/dist/index.d.mts.map +1 -1
  17. package/dist/index.mjs +5 -5
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/preview/index.d.mts +1 -1
  20. package/dist/preview/index.mjs +1 -1
  21. package/dist/{preview-D2dMmokw.mjs → preview-DyGw2b3_.mjs} +2 -2
  22. package/dist/{preview-D2dMmokw.mjs.map → preview-DyGw2b3_.mjs.map} +1 -1
  23. package/dist/preview-engine/audio.mjs +1 -1
  24. package/dist/preview-engine/{chunk-NTVDL3RS.mjs → chunk-AQNTIWTK.mjs} +1 -1
  25. package/dist/preview-engine/{chunk-5E5J4KT4.mjs → chunk-RHRMEBNK.mjs} +1 -1
  26. package/dist/preview-engine/chunk-VONDRBA2.mjs +1 -0
  27. package/dist/preview-engine/effects.mjs +1 -1
  28. package/dist/preview-engine/framefields.mjs +1 -1
  29. package/dist/preview-engine/player.mjs +1 -1
  30. package/dist/preview-engine/{renderers-U55KRIOV.mjs → renderers-EI6MQ2EL.mjs} +1 -1
  31. package/dist/preview-engine/signals.mjs +1 -1
  32. package/dist/renderer/index.d.mts +1 -1
  33. package/dist/renderer/index.mjs +1 -1
  34. package/dist/{renderer-Czxgaygb.mjs → renderer-BV48PFjX.mjs} +2 -2
  35. package/dist/{renderer-Czxgaygb.mjs.map → renderer-BV48PFjX.mjs.map} +1 -1
  36. package/dist/{renderer-CNb-DG00.mjs → renderer-CoWfz-9p.mjs} +373 -109
  37. package/dist/renderer-CoWfz-9p.mjs.map +1 -0
  38. package/dist/signals/index.mjs +1 -1
  39. package/package.json +57 -57
  40. package/dist/index-BxARm93R.d.mts.map +0 -1
  41. package/dist/index-C0o3lLyA.d.mts.map +0 -1
  42. package/dist/preview-engine/chunk-266DECPQ.mjs +0 -1
  43. package/dist/renderer-CNb-DG00.mjs.map +0 -1
package/README.md CHANGED
@@ -20,6 +20,16 @@
20
20
 
21
21
  </div>
22
22
 
23
+ **Add to your agent**
24
+
25
+ Teach Claude Code, Cursor, Codex, Copilot, Hermes, and 70+ others how to author, animate, and render compositions:
26
+
27
+ ```bash
28
+ npx skills add gatewai-dev/framefields
29
+ ```
30
+
31
+ ---
32
+
23
33
  Framefields is built for coding agents. It packs the capabilities usually split across three desktop creative suites—Photoshop-style compositing & VFX, After Effects-style typography & motion graphics, and Blender-style 3D scenes & cameras—into a single lightweight npm package.
24
34
 
25
35
  - **Zero Headless-Browser Overhead** — No Chromium, no Puppeteer, no DOM reflows, and no screenshot pipeline. Renders directly on native GPU hardware via Dawn / WebGPU / Metal / Vulkan.
@@ -68,22 +78,6 @@ Every frame is rendered by framefields directly from TypeScript in [`examples/`]
68
78
 
69
79
  ---
70
80
 
71
- ## Agent Skills
72
-
73
- Teach Claude Code, Cursor, Codex, Copilot, or Hermes how to author, animate, and render compositions:
74
-
75
- **Claude Code**
76
- ```text
77
- /plugin install framefields
78
- ```
79
-
80
- **Cursor, Codex, Hermes, Copilot & others**
81
- ```bash
82
- npx skills add gatewai-dev/framefields
83
- ```
84
-
85
- ---
86
-
87
81
  ## Installation
88
82
 
89
83
  ```bash
@@ -167,23 +161,7 @@ process.exit(0);
167
161
 
168
162
  ## AI Gateway
169
163
 
170
- Framefields includes optional, built-in access to the [**framefields.dev AI Gateway**](https://framefields.dev/models), letting agents and developers generate creative media—cinematic images, video footage, background music, voiceovers, and sound effects—directly from code. Authenticated via `FRAMEFIELDS_API_KEY` (an `ff_live_…` key from your [dashboard](https://framefields.dev/dashboard/keys)), the built-in `ai` client provides a unified interface (`run`, `subscribe`, `stream`, and `queue`) across hundreds of creative foundation models like Flux, Seedance, OpenAI, Nano Banana, Kling, Minimax, Suno, and ElevenLabs without configuring separate provider accounts or billing.
171
-
172
- To get started, store your API key in `.env` and create a dedicated script (such as `src/generate.ts`) that calls `ai.subscribe()` to render media into `assets/`. Because AI generation is asynchronous and billed per execution, assets are generated once, saved locally, and tracked with Git LFS—ready to be loaded cleanly into `Layer.image()`, `Layer.video()`, or `Layer.audio()` inside your frame-accurate compositions without recurring cost or latency during previews and renders. Browse the full model catalog and schemas at [**framefields.dev/models**](https://framefields.dev/models).
173
-
174
- ```typescript
175
- import { ai } from "framefields";
176
-
177
- // 1. Generate an image asset with Flux Schnell
178
- const still = await ai.subscribe<{ images: { url: string }[] }>("flux/schnell", {
179
- input: { prompt: "Cyberpunk alley in the rain, volumetric neon, 35mm", image_size: "landscape_16_9" },
180
- });
181
-
182
- // 2. Animate it into video with Kling or generate music & voiceovers
183
- const clip = await ai.subscribe<{ video: { url: string } }>("kling-video/v2.1/standard/image-to-video", {
184
- input: { prompt: "Slow forward tracking shot through neon rain", image_url: still.data.images[0].url, duration: "5" },
185
- });
186
- ```
164
+ Framefields provides built-in access to the [**AI Gateway**](https://framefields.dev/models), allowing agents and developers to generate cinematic video, images, music, and voiceovers directly from code using hundreds of hosted models. Explore the full model catalog in the [**AI Gateway**](https://framefields.dev/models) or read the integration guide in the [**documentation**](https://framefields.dev/docs/guides/ai/).
187
165
 
188
166
  ---
189
167
 
@@ -1,5 +1,5 @@
1
1
  import { N as WebGPUAudioProcessor } from "../index-DujpgUUq.mjs";
2
- import { a as SfxTrigger, c as softLimit, d as _default$5, f as _default$4, g as mixAudioTracks, h as _default$2, i as SfxRenderContext, l as _default$7, m as _default$3, n as encodeStereoWav, o as mixSfxInto, p as _default, r as RenderedSfx, s as renderSfx, t as EncodeWavOptions, u as _default$6 } from "../index-C0o3lLyA.mjs";
2
+ import { a as SfxTrigger, c as softLimit, d as _default$5, f as _default$4, g as mixAudioTracks, h as _default$2, i as SfxRenderContext, l as _default$7, m as _default$3, n as encodeStereoWav, o as mixSfxInto, p as _default, r as RenderedSfx, s as renderSfx, t as EncodeWavOptions, u as _default$6 } from "../index-B3-gwjK6.mjs";
3
3
  import "../index-B1XcyMm8.mjs";
4
4
  import { t as _default$1 } from "../renderer-CxqPeATd.mjs";
5
5
  export { EncodeWavOptions, RenderedSfx, SfxRenderContext, SfxTrigger, WebGPUAudioProcessor, _default as audioFadeRenderer, _default$1 as audioSignalExtractorRenderer, _default$2 as compressorRenderer, _default$3 as delayRenderer, encodeStereoWav, mixAudioTracks, mixSfxInto, _default$4 as noiseGateRenderer, _default$5 as parametricEqRenderer, renderSfx, _default$6 as reverbRenderer, softLimit, _default$7 as stereoPanningRenderer };
@@ -1,7 +1,7 @@
1
1
  import "../chunk-GBe2k3y8.mjs";
2
2
  import { g as WebGPUAudioProcessor } from "../dist-CspOG4V1.mjs";
3
3
  import { t as renderers_default$1 } from "../renderers-CP8CFWDs-DzGXDs5p.mjs";
4
- import { i as mixAudioTracks } from "../dist-CrK3rmyN.mjs";
4
+ import { i as mixAudioTracks } from "../dist-CxGfrigI.mjs";
5
5
  import { t as renderers_default$2 } from "../renderer-BMKFzfSQ.mjs";
6
6
  import { t as renderers_default$3 } from "../renderer-D2-V9nQq.mjs";
7
7
  import { t as renderers_default } from "../renderer-CzRqzp8P.mjs";
@@ -1,6 +1,6 @@
1
1
  import { xt as frameArraySignal } from "./dist-ClyC_d66.mjs";
2
2
  import { A as ensureDevice, St as AudioSignalComputePipeline } from "./dist-CspOG4V1.mjs";
3
- import { n as decodeAudioSource } from "./dist-CrK3rmyN.mjs";
3
+ import { n as decodeAudioSource } from "./dist-CxGfrigI.mjs";
4
4
 
5
5
  //#region src/signals/audio.ts
6
6
  const AudioSignal = {
@@ -82,4 +82,4 @@ const AudioSignal = {
82
82
 
83
83
  //#endregion
84
84
  export { AudioSignal as t };
85
- //# sourceMappingURL=audio-CPApYcWF.mjs.map
85
+ //# sourceMappingURL=audio-Bb31xJuX.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"audio-CPApYcWF.mjs","names":[],"sources":["../src/signals/audio.ts"],"sourcesContent":["import { decodeAudioSource } from \"@framefields/compositions\";\nimport {\n\ttype FrameSignal,\n\tframeArraySignal,\n\ttype GPUTextureBinding,\n} from \"@framefields/core\";\nimport {\n\ttype AudioSignalComputeConfig,\n\tAudioSignalComputePipeline,\n\tensureDevice,\n} from \"@framefields/webgpu-renderers\";\n\nexport interface AudioSignalOptions extends AudioSignalComputeConfig {\n\tfps?: number;\n\ttargetWidth?: number;\n\tpreviewMode?: \"waveform\" | \"envelope\" | \"beat_markers\" | \"spectrum\";\n}\n\nexport interface ExtractedAudioSignalsBundle {\n\tprimary: FrameSignal<number>;\n\tbeat: FrameSignal<number>;\n\tbass: FrameSignal<number>;\n\tenergy: FrameSignal<number>;\n\tdurationSec: number;\n\tnumFrames: number;\n\tsampleRate: number;\n\ttextureView: unknown;\n\ttexture: unknown;\n\tprimaryBuffer: unknown;\n\tbeatBuffer: unknown;\n\tbassBuffer: unknown;\n\tenergyBuffer: unknown;\n\tseekFrame(frame: number): void;\n}\n\nexport const AudioSignal = {\n\tasync extractFromChannels(\n\t\tchannels: Float32Array[],\n\t\tsampleRate: number,\n\t\toptions: AudioSignalOptions = {},\n\t): Promise<ExtractedAudioSignalsBundle> {\n\t\tconst device = await ensureDevice();\n\t\tconst fps = options.fps ?? 24;\n\t\tconst targetWidth = options.targetWidth ?? 512;\n\n\t\tconst extracted = await AudioSignalComputePipeline.extractFeatures(\n\t\t\tdevice,\n\t\t\tchannels,\n\t\t\tsampleRate,\n\t\t\tfps,\n\t\t\toptions,\n\t\t\ttargetWidth,\n\t\t);\n\n\t\tconst samples = extracted.channelSamples ?? {\n\t\t\tprimary: new Float32Array(extracted.numFrames),\n\t\t\tbeat: new Float32Array(extracted.numFrames),\n\t\t\tbass: new Float32Array(extracted.numFrames),\n\t\t\tenergy: new Float32Array(extracted.numFrames),\n\t\t};\n\n\t\tconst primaryBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_primary\",\n\t\t\tchannel: \"primary\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.primaryBuffer,\n\t\t\tstats: extracted.stats.primary,\n\t\t};\n\t\tconst beatBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_beat\",\n\t\t\tchannel: \"beat\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.beatBuffer,\n\t\t\tstats: extracted.stats.beat,\n\t\t};\n\t\tconst bassBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_bass\",\n\t\t\tchannel: \"bass\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.bassBuffer,\n\t\t\tstats: extracted.stats.bass,\n\t\t};\n\t\tconst energyBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_energy\",\n\t\t\tchannel: \"energy\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.energyBuffer,\n\t\t\tstats: extracted.stats.energy,\n\t\t};\n\n\t\tconst primary = frameArraySignal(samples.primary, fps, primaryBinding);\n\t\tconst beat = frameArraySignal(samples.beat, fps, beatBinding);\n\t\tconst bass = frameArraySignal(samples.bass, fps, bassBinding);\n\t\tconst energy = frameArraySignal(samples.energy, fps, energyBinding);\n\n\t\treturn {\n\t\t\tprimary,\n\t\t\tbeat,\n\t\t\tbass,\n\t\t\tenergy,\n\t\t\tdurationSec: extracted.durationSec,\n\t\t\tnumFrames: extracted.numFrames,\n\t\t\tsampleRate,\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tprimaryBuffer: extracted.primaryBuffer,\n\t\t\tbeatBuffer: extracted.beatBuffer,\n\t\t\tbassBuffer: extracted.bassBuffer,\n\t\t\tenergyBuffer: extracted.energyBuffer,\n\t\t\tseekFrame(frame: number) {\n\t\t\t\tprimary.seekFrame?.(frame);\n\t\t\t\tbeat.seekFrame?.(frame);\n\t\t\t\tbass.seekFrame?.(frame);\n\t\t\t\tenergy.seekFrame?.(frame);\n\t\t\t},\n\t\t};\n\t},\n\n\tasync extract(\n\t\tsource: string,\n\t\toptions: AudioSignalOptions = {},\n\t): Promise<ExtractedAudioSignalsBundle> {\n\t\tconst decoded = await decodeAudioSource(source);\n\t\tif (!decoded || decoded.channels.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to decode audio source for feature extraction: ${source}`,\n\t\t\t);\n\t\t}\n\t\treturn AudioSignal.extractFromChannels(\n\t\t\tdecoded.channels,\n\t\t\tdecoded.sampleRate,\n\t\t\toptions,\n\t\t);\n\t},\n};\n"],"mappings":";;;;;AAmCA,MAAa,cAAc;CAC1B,MAAM,oBACL,UACA,YACA,UAA8B,EAAE,EACO;EACvC,MAAM,SAAS,MAAM,cAAc;EACnC,MAAM,MAAM,QAAQ,OAAO;EAC3B,MAAM,cAAc,QAAQ,eAAe;EAE3C,MAAM,YAAY,MAAM,2BAA2B,gBAClD,QACA,UACA,YACA,KACA,SACA,YACA;EAED,MAAM,UAAU,UAAU,kBAAkB;GAC3C,SAAS,IAAI,aAAa,UAAU,UAAU;GAC9C,MAAM,IAAI,aAAa,UAAU,UAAU;GAC3C,MAAM,IAAI,aAAa,UAAU,UAAU;GAC3C,QAAQ,IAAI,aAAa,UAAU,UAAU;GAC7C;EAED,MAAM,iBAAoC;GACzC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EACD,MAAM,cAAiC;GACtC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EACD,MAAM,cAAiC;GACtC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EACD,MAAM,gBAAmC;GACxC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EAED,MAAM,UAAU,iBAAiB,QAAQ,SAAS,KAAK,eAAe;EACtE,MAAM,OAAO,iBAAiB,QAAQ,MAAM,KAAK,YAAY;EAC7D,MAAM,OAAO,iBAAiB,QAAQ,MAAM,KAAK,YAAY;EAC7D,MAAM,SAAS,iBAAiB,QAAQ,QAAQ,KAAK,cAAc;AAEnE,SAAO;GACN;GACA;GACA;GACA;GACA,aAAa,UAAU;GACvB,WAAW,UAAU;GACrB;GACA,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,eAAe,UAAU;GACzB,YAAY,UAAU;GACtB,YAAY,UAAU;GACtB,cAAc,UAAU;GACxB,UAAU,OAAe;AACxB,YAAQ,YAAY,MAAM;AAC1B,SAAK,YAAY,MAAM;AACvB,SAAK,YAAY,MAAM;AACvB,WAAO,YAAY,MAAM;;GAE1B;;CAGF,MAAM,QACL,QACA,UAA8B,EAAE,EACO;EACvC,MAAM,UAAU,MAAM,kBAAkB,OAAO;AAC/C,MAAI,CAAC,WAAW,QAAQ,SAAS,WAAW,EAC3C,OAAM,IAAI,MACT,yDAAyD,SACzD;AAEF,SAAO,YAAY,oBAClB,QAAQ,UACR,QAAQ,YACR,QACA;;CAEF"}
1
+ {"version":3,"file":"audio-Bb31xJuX.mjs","names":[],"sources":["../src/signals/audio.ts"],"sourcesContent":["import { decodeAudioSource } from \"@framefields/compositions\";\nimport {\n\ttype FrameSignal,\n\tframeArraySignal,\n\ttype GPUTextureBinding,\n} from \"@framefields/core\";\nimport {\n\ttype AudioSignalComputeConfig,\n\tAudioSignalComputePipeline,\n\tensureDevice,\n} from \"@framefields/webgpu-renderers\";\n\nexport interface AudioSignalOptions extends AudioSignalComputeConfig {\n\tfps?: number;\n\ttargetWidth?: number;\n\tpreviewMode?: \"waveform\" | \"envelope\" | \"beat_markers\" | \"spectrum\";\n}\n\nexport interface ExtractedAudioSignalsBundle {\n\tprimary: FrameSignal<number>;\n\tbeat: FrameSignal<number>;\n\tbass: FrameSignal<number>;\n\tenergy: FrameSignal<number>;\n\tdurationSec: number;\n\tnumFrames: number;\n\tsampleRate: number;\n\ttextureView: unknown;\n\ttexture: unknown;\n\tprimaryBuffer: unknown;\n\tbeatBuffer: unknown;\n\tbassBuffer: unknown;\n\tenergyBuffer: unknown;\n\tseekFrame(frame: number): void;\n}\n\nexport const AudioSignal = {\n\tasync extractFromChannels(\n\t\tchannels: Float32Array[],\n\t\tsampleRate: number,\n\t\toptions: AudioSignalOptions = {},\n\t): Promise<ExtractedAudioSignalsBundle> {\n\t\tconst device = await ensureDevice();\n\t\tconst fps = options.fps ?? 24;\n\t\tconst targetWidth = options.targetWidth ?? 512;\n\n\t\tconst extracted = await AudioSignalComputePipeline.extractFeatures(\n\t\t\tdevice,\n\t\t\tchannels,\n\t\t\tsampleRate,\n\t\t\tfps,\n\t\t\toptions,\n\t\t\ttargetWidth,\n\t\t);\n\n\t\tconst samples = extracted.channelSamples ?? {\n\t\t\tprimary: new Float32Array(extracted.numFrames),\n\t\t\tbeat: new Float32Array(extracted.numFrames),\n\t\t\tbass: new Float32Array(extracted.numFrames),\n\t\t\tenergy: new Float32Array(extracted.numFrames),\n\t\t};\n\n\t\tconst primaryBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_primary\",\n\t\t\tchannel: \"primary\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.primaryBuffer,\n\t\t\tstats: extracted.stats.primary,\n\t\t};\n\t\tconst beatBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_beat\",\n\t\t\tchannel: \"beat\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.beatBuffer,\n\t\t\tstats: extracted.stats.beat,\n\t\t};\n\t\tconst bassBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_bass\",\n\t\t\tchannel: \"bass\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.bassBuffer,\n\t\t\tstats: extracted.stats.bass,\n\t\t};\n\t\tconst energyBinding: GPUTextureBinding = {\n\t\t\tnodeId: \"audio_energy\",\n\t\t\tchannel: \"energy\",\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tbuffer: extracted.energyBuffer,\n\t\t\tstats: extracted.stats.energy,\n\t\t};\n\n\t\tconst primary = frameArraySignal(samples.primary, fps, primaryBinding);\n\t\tconst beat = frameArraySignal(samples.beat, fps, beatBinding);\n\t\tconst bass = frameArraySignal(samples.bass, fps, bassBinding);\n\t\tconst energy = frameArraySignal(samples.energy, fps, energyBinding);\n\n\t\treturn {\n\t\t\tprimary,\n\t\t\tbeat,\n\t\t\tbass,\n\t\t\tenergy,\n\t\t\tdurationSec: extracted.durationSec,\n\t\t\tnumFrames: extracted.numFrames,\n\t\t\tsampleRate,\n\t\t\ttextureView: extracted.textureView,\n\t\t\ttexture: extracted.texture,\n\t\t\tprimaryBuffer: extracted.primaryBuffer,\n\t\t\tbeatBuffer: extracted.beatBuffer,\n\t\t\tbassBuffer: extracted.bassBuffer,\n\t\t\tenergyBuffer: extracted.energyBuffer,\n\t\t\tseekFrame(frame: number) {\n\t\t\t\tprimary.seekFrame?.(frame);\n\t\t\t\tbeat.seekFrame?.(frame);\n\t\t\t\tbass.seekFrame?.(frame);\n\t\t\t\tenergy.seekFrame?.(frame);\n\t\t\t},\n\t\t};\n\t},\n\n\tasync extract(\n\t\tsource: string,\n\t\toptions: AudioSignalOptions = {},\n\t): Promise<ExtractedAudioSignalsBundle> {\n\t\tconst decoded = await decodeAudioSource(source);\n\t\tif (!decoded || decoded.channels.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`Failed to decode audio source for feature extraction: ${source}`,\n\t\t\t);\n\t\t}\n\t\treturn AudioSignal.extractFromChannels(\n\t\t\tdecoded.channels,\n\t\t\tdecoded.sampleRate,\n\t\t\toptions,\n\t\t);\n\t},\n};\n"],"mappings":";;;;;AAmCA,MAAa,cAAc;CAC1B,MAAM,oBACL,UACA,YACA,UAA8B,EAAE,EACO;EACvC,MAAM,SAAS,MAAM,cAAc;EACnC,MAAM,MAAM,QAAQ,OAAO;EAC3B,MAAM,cAAc,QAAQ,eAAe;EAE3C,MAAM,YAAY,MAAM,2BAA2B,gBAClD,QACA,UACA,YACA,KACA,SACA,YACA;EAED,MAAM,UAAU,UAAU,kBAAkB;GAC3C,SAAS,IAAI,aAAa,UAAU,UAAU;GAC9C,MAAM,IAAI,aAAa,UAAU,UAAU;GAC3C,MAAM,IAAI,aAAa,UAAU,UAAU;GAC3C,QAAQ,IAAI,aAAa,UAAU,UAAU;GAC7C;EAED,MAAM,iBAAoC;GACzC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EACD,MAAM,cAAiC;GACtC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EACD,MAAM,cAAiC;GACtC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EACD,MAAM,gBAAmC;GACxC,QAAQ;GACR,SAAS;GACT,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,QAAQ,UAAU;GAClB,OAAO,UAAU,MAAM;GACvB;EAED,MAAM,UAAU,iBAAiB,QAAQ,SAAS,KAAK,eAAe;EACtE,MAAM,OAAO,iBAAiB,QAAQ,MAAM,KAAK,YAAY;EAC7D,MAAM,OAAO,iBAAiB,QAAQ,MAAM,KAAK,YAAY;EAC7D,MAAM,SAAS,iBAAiB,QAAQ,QAAQ,KAAK,cAAc;AAEnE,SAAO;GACN;GACA;GACA;GACA;GACA,aAAa,UAAU;GACvB,WAAW,UAAU;GACrB;GACA,aAAa,UAAU;GACvB,SAAS,UAAU;GACnB,eAAe,UAAU;GACzB,YAAY,UAAU;GACtB,YAAY,UAAU;GACtB,cAAc,UAAU;GACxB,UAAU,OAAe;AACxB,YAAQ,YAAY,MAAM;AAC1B,SAAK,YAAY,MAAM;AACvB,SAAK,YAAY,MAAM;AACvB,WAAO,YAAY,MAAM;;GAE1B;;CAGF,MAAM,QACL,QACA,UAA8B,EAAE,EACO;EACvC,MAAM,UAAU,MAAM,kBAAkB,OAAO;AAC/C,MAAI,CAAC,WAAW,QAAQ,SAAS,WAAW,EAC3C,OAAM,IAAI,MACT,yDAAyD,SACzD;AAEF,SAAO,YAAY,oBAClB,QAAQ,UACR,QAAQ,YACR,QACA;;CAEF"}
@@ -1293,11 +1293,24 @@ var MixBus = class {
1293
1293
  for (let c = 0; c < 2; c++) addInto(dest[c], src[c], start);
1294
1294
  }
1295
1295
  };
1296
+ /** A clip's file name for progress, without paths or signed-URL queries. */
1297
+ function sourceLabel(url) {
1298
+ if (url.startsWith("data:")) return "inline audio";
1299
+ const name = url.split(/[?#]/)[0].split(/[\\/]/).pop();
1300
+ return name ? decodeURIComponentSafe(name) : "audio";
1301
+ }
1302
+ function decodeURIComponentSafe(s) {
1303
+ try {
1304
+ return decodeURIComponent(s);
1305
+ } catch {
1306
+ return s;
1307
+ }
1308
+ }
1296
1309
  /**
1297
1310
  * Recursively parses the VirtualMediaData tree and mixes all active audio tracks
1298
1311
  * into a single unified stereo PCM stream (Float32Array channels).
1299
1312
  */
1300
- async function mixAudioTracks(virtualMedia, fps, targetSampleRate = 48e3, device, renderId) {
1313
+ async function mixAudioTracks(virtualMedia, fps, targetSampleRate = 48e3, device, renderId, options = {}) {
1301
1314
  mediaLogger.debug(`[AudioExtractor] mixAudioTracks started. FPS: ${fps}, targetSampleRate: ${targetSampleRate}`);
1302
1315
  await waitForDelayRender();
1303
1316
  shaderStore.registerSignalsFromComposition(renderId, virtualMedia);
@@ -1414,6 +1427,14 @@ async function mixAudioTracks(virtualMedia, fps, targetSampleRate = 48e3, device
1414
1427
  }
1415
1428
  await yieldToMain();
1416
1429
  }
1430
+ if (mediaType === "Audio" || mediaType === "Video") {
1431
+ clipsDone++;
1432
+ options.onProgress?.({
1433
+ done: clipsDone,
1434
+ total: clipsTotal,
1435
+ ...params.sourceUrl && { source: sourceLabel(params.sourceUrl) }
1436
+ });
1437
+ }
1417
1438
  }
1418
1439
  if (node.children) for (const child of node.children) {
1419
1440
  const childChannels = await traverse(child, totalSeekSec, totalClockFrames, volume, speedMultiplier, actualDurationFrames);
@@ -1451,7 +1472,26 @@ async function mixAudioTracks(virtualMedia, fps, targetSampleRate = 48e3, device
1451
1472
  } else await processSegment(null, 0);
1452
1473
  return bus.channels;
1453
1474
  }
1454
- const rootChannels = await traverse(normalizeTimeline(virtualMedia, fps), 0, 0, 1, 1, null);
1475
+ const normalized = normalizeTimeline(virtualMedia, fps);
1476
+ let clipsTotal = 0;
1477
+ let clipsDone = 0;
1478
+ if (options.onProgress) {
1479
+ const count = (node) => {
1480
+ if (!node) return;
1481
+ const op = node.operation;
1482
+ if (op?.op === "source") {
1483
+ const mediaType = getMediaType(node);
1484
+ if (mediaType === "Audio" || mediaType === "Video") clipsTotal += Math.max(1, op.timeline?.segments?.length ?? 0);
1485
+ }
1486
+ for (const child of node.children ?? []) count(child);
1487
+ };
1488
+ count(normalized);
1489
+ options.onProgress({
1490
+ done: 0,
1491
+ total: clipsTotal
1492
+ });
1493
+ }
1494
+ const rootChannels = await traverse(normalized, 0, 0, 1, 1, null);
1455
1495
  const mixedChannels = rootChannels?.every((ch) => ch.length === totalTargetSamples) && rootChannels[0] !== rootChannels[1] ? rootChannels : [new Float32Array(totalTargetSamples), new Float32Array(totalTargetSamples)];
1456
1496
  const [outputLeft, outputRight] = mixedChannels;
1457
1497
  if (rootChannels && mixedChannels !== rootChannels) for (let c = 0; c < 2; c++) {
@@ -1483,4 +1523,4 @@ async function mixAudioTracks(virtualMedia, fps, targetSampleRate = 48e3, device
1483
1523
 
1484
1524
  //#endregion
1485
1525
  export { spring as _, resolveLayerDuration as a, computeStrokeRect as c, measureSpring as d, measureText as f, requiresIsolation as g, preloadFont as h, mixAudioTracks as i, interpolate as l, preloadCompositionFonts as m, decodeAudioSource as n, Easing as o, measureTextAsync as p, drawCompositionTree as r, bezier as s, compositionStateStore as t, mapBlendModeToSkia as u, springCalculation as v };
1486
- //# sourceMappingURL=dist-CrK3rmyN.mjs.map
1526
+ //# sourceMappingURL=dist-CxGfrigI.mjs.map