@meframe/core 0.0.2 → 0.0.4

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 (193) hide show
  1. package/dist/Meframe.d.ts.map +1 -1
  2. package/dist/Meframe.js +6 -4
  3. package/dist/Meframe.js.map +1 -1
  4. package/dist/cache/CacheManager.d.ts +2 -2
  5. package/dist/cache/CacheManager.d.ts.map +1 -1
  6. package/dist/cache/CacheManager.js +4 -3
  7. package/dist/cache/CacheManager.js.map +1 -1
  8. package/dist/cache/l1/VideoL1Cache.d.ts +2 -2
  9. package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
  10. package/dist/cache/l1/VideoL1Cache.js +13 -8
  11. package/dist/cache/l1/VideoL1Cache.js.map +1 -1
  12. package/dist/config/defaults.d.ts.map +1 -1
  13. package/dist/config/defaults.js +3 -1
  14. package/dist/config/defaults.js.map +1 -1
  15. package/dist/config/types.d.ts +6 -0
  16. package/dist/config/types.d.ts.map +1 -1
  17. package/dist/controllers/PlaybackController.d.ts +7 -8
  18. package/dist/controllers/PlaybackController.d.ts.map +1 -1
  19. package/dist/controllers/PlaybackController.js +56 -76
  20. package/dist/controllers/PlaybackController.js.map +1 -1
  21. package/dist/controllers/types.d.ts +2 -3
  22. package/dist/controllers/types.d.ts.map +1 -1
  23. package/dist/event/events.d.ts +1 -4
  24. package/dist/event/events.d.ts.map +1 -1
  25. package/dist/event/events.js.map +1 -1
  26. package/dist/model/CompositionModel.d.ts +1 -0
  27. package/dist/model/CompositionModel.d.ts.map +1 -1
  28. package/dist/model/CompositionModel.js +2 -0
  29. package/dist/model/CompositionModel.js.map +1 -1
  30. package/dist/model/patch.d.ts +6 -2
  31. package/dist/model/patch.d.ts.map +1 -1
  32. package/dist/model/patch.js +76 -2
  33. package/dist/model/patch.js.map +1 -1
  34. package/dist/model/types.d.ts +1 -0
  35. package/dist/model/types.d.ts.map +1 -1
  36. package/dist/orchestrator/CompositionPlanner.d.ts +8 -7
  37. package/dist/orchestrator/CompositionPlanner.d.ts.map +1 -1
  38. package/dist/orchestrator/CompositionPlanner.js +33 -56
  39. package/dist/orchestrator/CompositionPlanner.js.map +1 -1
  40. package/dist/orchestrator/Orchestrator.d.ts +0 -1
  41. package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
  42. package/dist/orchestrator/Orchestrator.js +40 -19
  43. package/dist/orchestrator/Orchestrator.js.map +1 -1
  44. package/dist/orchestrator/VideoClipSession.d.ts +3 -5
  45. package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
  46. package/dist/orchestrator/VideoClipSession.js +66 -69
  47. package/dist/orchestrator/VideoClipSession.js.map +1 -1
  48. package/dist/orchestrator/types.d.ts +2 -0
  49. package/dist/orchestrator/types.d.ts.map +1 -1
  50. package/dist/stages/compose/GlobalAudioSession.d.ts +6 -0
  51. package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -1
  52. package/dist/stages/compose/GlobalAudioSession.js +17 -1
  53. package/dist/stages/compose/GlobalAudioSession.js.map +1 -1
  54. package/dist/stages/compose/types.d.ts +2 -1
  55. package/dist/stages/compose/types.d.ts.map +1 -1
  56. package/dist/stages/demux/MP4Demuxer.d.ts +0 -1
  57. package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
  58. package/dist/stages/load/ResourceLoader.d.ts +22 -1
  59. package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
  60. package/dist/stages/load/ResourceLoader.js +71 -25
  61. package/dist/stages/load/ResourceLoader.js.map +1 -1
  62. package/dist/stages/load/TaskManager.d.ts +1 -1
  63. package/dist/stages/load/TaskManager.d.ts.map +1 -1
  64. package/dist/stages/load/TaskManager.js +3 -2
  65. package/dist/stages/load/TaskManager.js.map +1 -1
  66. package/dist/stages/load/types.d.ts +2 -0
  67. package/dist/stages/load/types.d.ts.map +1 -1
  68. package/dist/utils/time-utils.d.ts +3 -2
  69. package/dist/utils/time-utils.d.ts.map +1 -1
  70. package/dist/utils/time-utils.js +2 -1
  71. package/dist/utils/time-utils.js.map +1 -1
  72. package/dist/vite-plugin.d.ts +5 -3
  73. package/dist/vite-plugin.d.ts.map +1 -1
  74. package/dist/vite-plugin.js +109 -52
  75. package/dist/vite-plugin.js.map +1 -1
  76. package/dist/worker/WorkerPool.d.ts +9 -0
  77. package/dist/worker/WorkerPool.d.ts.map +1 -1
  78. package/dist/worker/WorkerPool.js +32 -5
  79. package/dist/worker/WorkerPool.js.map +1 -1
  80. package/dist/{stages/demux → workers}/MP4Demuxer.js +4 -13
  81. package/dist/workers/MP4Demuxer.js.map +1 -0
  82. package/dist/workers/WorkerChannel.js +486 -0
  83. package/dist/workers/WorkerChannel.js.map +1 -0
  84. package/dist/{assets/video-demux.worker-D019I7GQ.js → workers/mp4box.all.js} +4 -912
  85. package/dist/workers/mp4box.all.js.map +1 -0
  86. package/dist/{assets/audio-compose.worker-nGVvHD5Q.js → workers/stages/compose/audio-compose.worker.js} +7 -481
  87. package/dist/workers/stages/compose/audio-compose.worker.js.map +1 -0
  88. package/dist/{assets/video-compose.worker-DPzsC21d.js → workers/stages/compose/video-compose.worker.js} +120 -562
  89. package/dist/workers/stages/compose/video-compose.worker.js.map +1 -0
  90. package/dist/{assets/decode.worker-DpWHsc7R.js → workers/stages/decode/decode.worker.js} +7 -481
  91. package/dist/workers/stages/decode/decode.worker.js.map +1 -0
  92. package/dist/{stages → workers/stages}/demux/audio-demux.worker.js +184 -4
  93. package/dist/workers/stages/demux/audio-demux.worker.js.map +1 -0
  94. package/dist/{stages → workers/stages}/demux/video-demux.worker.js +2 -3
  95. package/dist/workers/stages/demux/video-demux.worker.js.map +1 -0
  96. package/dist/{stages → workers/stages}/encode/encode.worker.js +238 -4
  97. package/dist/workers/stages/encode/encode.worker.js.map +1 -0
  98. package/dist/{stages/mux/MP4Muxer.js → workers/stages/mux/mux.worker.js} +244 -5
  99. package/dist/workers/stages/mux/mux.worker.js.map +1 -0
  100. package/package.json +21 -21
  101. package/dist/assets/audio-compose.worker-nGVvHD5Q.js.map +0 -1
  102. package/dist/assets/audio-demux.worker-xwWBtbAe.js +0 -8299
  103. package/dist/assets/audio-demux.worker-xwWBtbAe.js.map +0 -1
  104. package/dist/assets/decode.worker-DpWHsc7R.js.map +0 -1
  105. package/dist/assets/encode.worker-nfOb3kw6.js +0 -1026
  106. package/dist/assets/encode.worker-nfOb3kw6.js.map +0 -1
  107. package/dist/assets/mux.worker-uEMQY066.js +0 -8019
  108. package/dist/assets/mux.worker-uEMQY066.js.map +0 -1
  109. package/dist/assets/video-compose.worker-DPzsC21d.js.map +0 -1
  110. package/dist/assets/video-demux.worker-D019I7GQ.js.map +0 -1
  111. package/dist/controllers/PreviewHandle.d.ts +0 -25
  112. package/dist/controllers/PreviewHandle.d.ts.map +0 -1
  113. package/dist/controllers/PreviewHandle.js +0 -45
  114. package/dist/controllers/PreviewHandle.js.map +0 -1
  115. package/dist/model/dirty-range.js +0 -220
  116. package/dist/model/dirty-range.js.map +0 -1
  117. package/dist/model/types.js +0 -5
  118. package/dist/model/types.js.map +0 -1
  119. package/dist/plugins/BackpressureMonitor.js +0 -62
  120. package/dist/plugins/BackpressureMonitor.js.map +0 -1
  121. package/dist/stages/compose/AudioDucker.js +0 -161
  122. package/dist/stages/compose/AudioDucker.js.map +0 -1
  123. package/dist/stages/compose/AudioMixer.js +0 -373
  124. package/dist/stages/compose/AudioMixer.js.map +0 -1
  125. package/dist/stages/compose/FilterProcessor.js +0 -226
  126. package/dist/stages/compose/FilterProcessor.js.map +0 -1
  127. package/dist/stages/compose/LayerRenderer.js +0 -215
  128. package/dist/stages/compose/LayerRenderer.js.map +0 -1
  129. package/dist/stages/compose/TransitionProcessor.js +0 -189
  130. package/dist/stages/compose/TransitionProcessor.js.map +0 -1
  131. package/dist/stages/compose/VideoComposer.js +0 -186
  132. package/dist/stages/compose/VideoComposer.js.map +0 -1
  133. package/dist/stages/compose/audio-compose.worker.d.ts +0 -79
  134. package/dist/stages/compose/audio-compose.worker.d.ts.map +0 -1
  135. package/dist/stages/compose/audio-compose.worker.js +0 -540
  136. package/dist/stages/compose/audio-compose.worker.js.map +0 -1
  137. package/dist/stages/compose/audio-compose.worker2.js +0 -5
  138. package/dist/stages/compose/audio-compose.worker2.js.map +0 -1
  139. package/dist/stages/compose/video-compose.worker.d.ts +0 -60
  140. package/dist/stages/compose/video-compose.worker.d.ts.map +0 -1
  141. package/dist/stages/compose/video-compose.worker.js +0 -379
  142. package/dist/stages/compose/video-compose.worker.js.map +0 -1
  143. package/dist/stages/compose/video-compose.worker2.js +0 -5
  144. package/dist/stages/compose/video-compose.worker2.js.map +0 -1
  145. package/dist/stages/decode/AudioChunkDecoder.js +0 -82
  146. package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
  147. package/dist/stages/decode/BaseDecoder.js +0 -130
  148. package/dist/stages/decode/BaseDecoder.js.map +0 -1
  149. package/dist/stages/decode/VideoChunkDecoder.js +0 -199
  150. package/dist/stages/decode/VideoChunkDecoder.js.map +0 -1
  151. package/dist/stages/decode/decode.worker.d.ts +0 -70
  152. package/dist/stages/decode/decode.worker.d.ts.map +0 -1
  153. package/dist/stages/decode/decode.worker.js +0 -423
  154. package/dist/stages/decode/decode.worker.js.map +0 -1
  155. package/dist/stages/decode/decode.worker2.js +0 -5
  156. package/dist/stages/decode/decode.worker2.js.map +0 -1
  157. package/dist/stages/demux/MP3FrameParser.js +0 -186
  158. package/dist/stages/demux/MP3FrameParser.js.map +0 -1
  159. package/dist/stages/demux/MP4Demuxer.js.map +0 -1
  160. package/dist/stages/demux/audio-demux.worker.d.ts +0 -51
  161. package/dist/stages/demux/audio-demux.worker.d.ts.map +0 -1
  162. package/dist/stages/demux/audio-demux.worker.js.map +0 -1
  163. package/dist/stages/demux/audio-demux.worker2.js +0 -5
  164. package/dist/stages/demux/audio-demux.worker2.js.map +0 -1
  165. package/dist/stages/demux/video-demux.worker.d.ts +0 -51
  166. package/dist/stages/demux/video-demux.worker.d.ts.map +0 -1
  167. package/dist/stages/demux/video-demux.worker.js.map +0 -1
  168. package/dist/stages/demux/video-demux.worker2.js +0 -5
  169. package/dist/stages/demux/video-demux.worker2.js.map +0 -1
  170. package/dist/stages/encode/AudioChunkEncoder.js +0 -37
  171. package/dist/stages/encode/AudioChunkEncoder.js.map +0 -1
  172. package/dist/stages/encode/BaseEncoder.js +0 -164
  173. package/dist/stages/encode/BaseEncoder.js.map +0 -1
  174. package/dist/stages/encode/VideoChunkEncoder.js +0 -50
  175. package/dist/stages/encode/VideoChunkEncoder.js.map +0 -1
  176. package/dist/stages/encode/encode.worker.d.ts +0 -3
  177. package/dist/stages/encode/encode.worker.d.ts.map +0 -1
  178. package/dist/stages/encode/encode.worker.js.map +0 -1
  179. package/dist/stages/encode/encode.worker2.js +0 -5
  180. package/dist/stages/encode/encode.worker2.js.map +0 -1
  181. package/dist/stages/mux/MP4Muxer.js.map +0 -1
  182. package/dist/stages/mux/mux.worker.d.ts +0 -65
  183. package/dist/stages/mux/mux.worker.d.ts.map +0 -1
  184. package/dist/stages/mux/mux.worker.js +0 -219
  185. package/dist/stages/mux/mux.worker.js.map +0 -1
  186. package/dist/stages/mux/mux.worker2.js +0 -5
  187. package/dist/stages/mux/mux.worker2.js.map +0 -1
  188. package/dist/stages/mux/utils.js +0 -34
  189. package/dist/stages/mux/utils.js.map +0 -1
  190. package/dist/worker/worker-registry.d.ts +0 -12
  191. package/dist/worker/worker-registry.d.ts.map +0 -1
  192. package/dist/worker/worker-registry.js +0 -20
  193. package/dist/worker/worker-registry.js.map +0 -1
@@ -1,79 +0,0 @@
1
- /**
2
- * AudioComposeWorker - Audio mixing and ducking stage
3
- * Mixes multiple audio tracks with volume, ducking, and effects
4
- *
5
- * Pipeline: DecodeWorker → AudioComposeWorker → EncodeWorker
6
- *
7
- * Features:
8
- * - Multi-track PCM mixing
9
- * - BGM ducking for voice tracks
10
- * - Stream-based processing with backpressure
11
- * - Real-time volume and effect adjustments
12
- */
13
- export declare class AudioComposeWorker {
14
- private channel;
15
- private mixer;
16
- private ducker;
17
- private mixStream;
18
- private decoderPort;
19
- private encoderPort;
20
- private trackBuffers;
21
- private trackQueueWaiters;
22
- private encoderStreamAttached;
23
- private readonly mixWindowUs;
24
- private readonly maxQueuedSegments;
25
- private mixing;
26
- constructor();
27
- private setupHandlers;
28
- /** Unified connect handler mapping for stream pipeline */
29
- private handleConnect;
30
- private handleReceiveStream;
31
- /**
32
- * Configure audio composer
33
- * @param payload.config - Audio composition configuration
34
- * @param payload.initial - If true, initialize worker state; otherwise just update config
35
- */
36
- private handleConfigure;
37
- /**
38
- * Connect to decoder worker to receive audio streams
39
- */
40
- /**
41
- * Add an audio track
42
- */
43
- private handleAddTrack;
44
- /**
45
- * Remove an audio track
46
- */
47
- private handleRemoveTrack;
48
- /**
49
- * Update track configuration
50
- */
51
- private handleUpdateTrack;
52
- /**
53
- * Configure audio ducking
54
- */
55
- private handleConfigureDucking;
56
- /**
57
- * Get mixer statistics
58
- */
59
- private handleGetStats;
60
- /**
61
- * Dispose worker and cleanup resources
62
- */
63
- private handleDispose;
64
- private attachEncodeStream;
65
- private bufferTrackStream;
66
- private scheduleMix;
67
- private computeNextWindow;
68
- private buildMixRequest;
69
- private consumeSegment;
70
- private hasBufferedAudio;
71
- private cloneTrackConfig;
72
- private applyTrackConfigPatch;
73
- private waitForQueueSpace;
74
- private resolveQueueWaiter;
75
- private disposeTrackBuffer;
76
- }
77
- declare const _default: null;
78
- export default _default;
79
- //# sourceMappingURL=audio-compose.worker.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"audio-compose.worker.d.ts","sourceRoot":"","sources":["../../../src/stages/compose/audio-compose.worker.ts"],"names":[],"mappings":"AA4BA;;;;;;;;;;;GAWG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,SAAS,CAAuD;IAGxE,OAAO,CAAC,WAAW,CAA4B;IAC/C,OAAO,CAAC,WAAW,CAA4B;IAE/C,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,iBAAiB,CAAwC;IACjE,OAAO,CAAC,qBAAqB,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAK;IACvC,OAAO,CAAC,MAAM,CAAS;;IAYvB,OAAO,CAAC,aAAa;IAarB,0DAA0D;YAC5C,aAAa;YAsCb,mBAAmB;IAmDjC;;;;OAIG;YACW,eAAe;IA+C7B;;OAEG;IACH;;OAEG;IACH,OAAO,CAAC,cAAc;IAsCtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkBzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6BzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAiB9B;;OAEG;YACW,cAAc;IAoB5B;;OAEG;YACW,aAAa;YAiBb,kBAAkB;YAkElB,iBAAiB;YA6CjB,WAAW;IAmCzB,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,eAAe;IAsCvB,OAAO,CAAC,cAAc;IAmCtB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,qBAAqB;YAqCf,iBAAiB;IAgB/B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,kBAAkB;CAsB3B;;AAUD,wBAAoB"}
@@ -1,540 +0,0 @@
1
- import { WorkerChannel } from "../../worker/WorkerChannel.js";
2
- import { WorkerMessageType, WorkerState } from "../../worker/types.js";
3
- import { AudioMixer } from "./AudioMixer.js";
4
- import { AudioDucker } from "./AudioDucker.js";
5
- class AudioComposeWorker {
6
- channel;
7
- mixer = null;
8
- ducker = null;
9
- mixStream = null;
10
- // Connections to other workers
11
- decoderPort = null;
12
- encoderPort = null;
13
- // Track buffer map
14
- trackBuffers = /* @__PURE__ */ new Map();
15
- trackQueueWaiters = /* @__PURE__ */ new Map();
16
- encoderStreamAttached = false;
17
- mixWindowUs = 4e4;
18
- // 40ms window
19
- maxQueuedSegments = 8;
20
- mixing = false;
21
- constructor() {
22
- this.channel = new WorkerChannel(self, {
23
- name: "AudioComposeWorker",
24
- timeout: 3e4
25
- });
26
- this.setupHandlers();
27
- }
28
- setupHandlers() {
29
- this.channel.registerHandler("configure", this.handleConfigure.bind(this));
30
- this.channel.registerHandler("connect", this.handleConnect.bind(this));
31
- this.channel.registerHandler("add_track", this.handleAddTrack.bind(this));
32
- this.channel.registerHandler("remove_track", this.handleRemoveTrack.bind(this));
33
- this.channel.registerHandler("update_track", this.handleUpdateTrack.bind(this));
34
- this.channel.registerHandler("configure_ducking", this.handleConfigureDucking.bind(this));
35
- this.channel.registerHandler("get_stats", this.handleGetStats.bind(this));
36
- this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));
37
- }
38
- /** Unified connect handler mapping for stream pipeline */
39
- async handleConnect(payload) {
40
- if (payload.direction === "upstream") {
41
- this.decoderPort = payload.port;
42
- const decoderChannel = new WorkerChannel(this.decoderPort, {
43
- name: "AudioCompose-Decoder",
44
- timeout: 3e4
45
- });
46
- decoderChannel.registerHandler("audio_track:add", this.handleAddTrack.bind(this));
47
- decoderChannel.registerHandler("audio_track:remove", this.handleRemoveTrack.bind(this));
48
- decoderChannel.registerHandler("audio_track:update", this.handleUpdateTrack.bind(this));
49
- decoderChannel.receiveStream(this.handleReceiveStream.bind(this));
50
- return { success: true };
51
- }
52
- if (payload.direction === "downstream") {
53
- this.encoderPort = payload.port;
54
- return { success: true };
55
- }
56
- return { success: true };
57
- }
58
- async handleReceiveStream(stream, metadata) {
59
- if (metadata?.streamType !== "audio" || !this.mixStream || !this.mixer) {
60
- return;
61
- }
62
- const update = {};
63
- const currentConfig = this.mixer.getConfig();
64
- if (typeof metadata?.sampleRate === "number" && metadata.sampleRate > 0) {
65
- if (!currentConfig.sampleRate || currentConfig.sampleRate !== metadata.sampleRate) {
66
- update.sampleRate = metadata.sampleRate;
67
- }
68
- }
69
- if (typeof metadata?.numberOfChannels === "number" && metadata.numberOfChannels > 0) {
70
- if (!currentConfig.numberOfChannels || metadata.numberOfChannels > currentConfig.numberOfChannels) {
71
- update.numberOfChannels = metadata.numberOfChannels;
72
- }
73
- }
74
- if (Object.keys(update).length > 0) {
75
- this.mixer.updateConfig(update);
76
- }
77
- const mixerConfig = this.mixer.getConfig();
78
- const streamMetadata = {
79
- ...metadata,
80
- streamType: "audio",
81
- sampleRate: mixerConfig.sampleRate,
82
- numberOfChannels: mixerConfig.numberOfChannels
83
- };
84
- await this.attachEncodeStream(streamMetadata);
85
- const trackId = metadata?.trackId ?? metadata?.clipId;
86
- if (!trackId) {
87
- console.warn("[AudioComposeWorker] Missing track identifier in audio stream metadata");
88
- await stream.cancel();
89
- return;
90
- }
91
- await this.bufferTrackStream(trackId, stream, streamMetadata);
92
- this.scheduleMix();
93
- }
94
- /**
95
- * Configure audio composer
96
- * @param payload.config - Audio composition configuration
97
- * @param payload.initial - If true, initialize worker state; otherwise just update config
98
- */
99
- async handleConfigure(payload) {
100
- const { config, initial = false } = payload;
101
- try {
102
- if (initial) {
103
- this.channel.state = WorkerState.Ready;
104
- this.mixer = new AudioMixer(config);
105
- this.ducker = new AudioDucker(config.sampleRate);
106
- this.mixStream = this.mixer.createMixStream(this.ducker);
107
- this.channel.notify("configured", {
108
- sampleRate: config.sampleRate,
109
- numberOfChannels: config.numberOfChannels
110
- });
111
- } else {
112
- if (!this.mixer || !this.ducker) {
113
- throw {
114
- code: "NOT_INITIALIZED",
115
- message: "Audio composer not initialized. Call configure with initial=true first"
116
- };
117
- }
118
- }
119
- return { success: true };
120
- } catch (error) {
121
- throw {
122
- code: error.code || "CONFIG_ERROR",
123
- message: error.message
124
- };
125
- }
126
- }
127
- /**
128
- * Connect to decoder worker to receive audio streams
129
- */
130
- /**
131
- * Add an audio track
132
- */
133
- handleAddTrack(payload) {
134
- if (!this.mixer) {
135
- throw {
136
- code: "NOT_CONFIGURED",
137
- message: "Mixer not configured"
138
- };
139
- }
140
- const config = this.cloneTrackConfig(payload.config);
141
- const track = {
142
- id: payload.trackId,
143
- clipId: payload.clipId,
144
- type: payload.type ?? "other",
145
- config
146
- };
147
- this.mixer.addTrack(track);
148
- this.trackBuffers.set(payload.trackId, {
149
- clipId: payload.clipId,
150
- queue: [],
151
- ended: false,
152
- config,
153
- type: payload.type ?? "other"
154
- });
155
- this.channel.notify("track_added", {
156
- trackId: track.id,
157
- trackType: track.type
158
- });
159
- return { success: true };
160
- }
161
- /**
162
- * Remove an audio track
163
- */
164
- handleRemoveTrack(payload) {
165
- if (!this.mixer) {
166
- throw {
167
- code: "NOT_CONFIGURED",
168
- message: "Mixer not configured"
169
- };
170
- }
171
- this.mixer.removeTrack(payload.trackId);
172
- this.disposeTrackBuffer(payload.trackId);
173
- this.channel.notify("track_removed", {
174
- trackId: payload.trackId
175
- });
176
- return { success: true };
177
- }
178
- /**
179
- * Update track configuration
180
- */
181
- handleUpdateTrack(payload) {
182
- if (!this.mixer) {
183
- throw {
184
- code: "NOT_CONFIGURED",
185
- message: "Mixer not configured"
186
- };
187
- }
188
- this.mixer.updateTrack(payload.trackId, payload.config);
189
- const buffer = this.trackBuffers.get(payload.trackId);
190
- if (buffer) {
191
- if (payload.config.type) {
192
- buffer.type = payload.config.type;
193
- }
194
- this.applyTrackConfigPatch(buffer.config, payload.config);
195
- }
196
- this.channel.notify("track_updated", {
197
- trackId: payload.trackId
198
- });
199
- return { success: true };
200
- }
201
- /**
202
- * Configure audio ducking
203
- */
204
- handleConfigureDucking(config) {
205
- if (!this.ducker) {
206
- throw {
207
- code: "NOT_CONFIGURED",
208
- message: "Ducker not configured"
209
- };
210
- }
211
- this.ducker.configure(config);
212
- this.channel.notify("ducking_configured", {
213
- enabled: config.enabled
214
- });
215
- return { success: true };
216
- }
217
- /**
218
- * Get mixer statistics
219
- */
220
- async handleGetStats() {
221
- if (!this.mixer) {
222
- return { state: this.channel.state };
223
- }
224
- return {
225
- tracks: this.mixer.tracks,
226
- ducking: this.ducker ? {
227
- configured: this.ducker !== null
228
- } : null,
229
- state: this.channel.state
230
- };
231
- }
232
- /**
233
- * Dispose worker and cleanup resources
234
- */
235
- async handleDispose() {
236
- this.mixer = null;
237
- this.ducker = null;
238
- this.mixStream = null;
239
- this.decoderPort?.close();
240
- this.decoderPort = null;
241
- this.encoderPort?.close();
242
- this.encoderPort = null;
243
- this.channel.state = WorkerState.Disposed;
244
- return { success: true };
245
- }
246
- async attachEncodeStream(metadata) {
247
- if (!this.mixStream || !this.encoderPort || this.encoderStreamAttached || !this.mixer) {
248
- return;
249
- }
250
- const encoderChannel = new WorkerChannel(this.encoderPort, {
251
- name: "AudioCompose-Encoder",
252
- timeout: 3e4
253
- });
254
- const mixerConfig = this.mixer?.getConfig();
255
- const streamMetadata = {
256
- ...metadata,
257
- streamType: "audio",
258
- sampleRate: mixerConfig?.sampleRate ?? metadata?.sampleRate,
259
- numberOfChannels: mixerConfig?.numberOfChannels ?? metadata?.numberOfChannels
260
- };
261
- const [encoderResultStream, previewResultStream] = this.mixStream.readable.tee();
262
- const createAudioDataStream = (source) => {
263
- return new ReadableStream({
264
- start: (controller) => {
265
- const reader = source.getReader();
266
- const pump = async () => {
267
- const { done, value } = await reader.read();
268
- if (done) {
269
- reader.releaseLock();
270
- controller.close();
271
- return;
272
- }
273
- try {
274
- controller.enqueue(value.audioData);
275
- } catch (error) {
276
- controller.error(error);
277
- reader.releaseLock();
278
- return;
279
- }
280
- await pump();
281
- };
282
- pump().catch((error) => {
283
- reader.releaseLock();
284
- controller.error(error);
285
- });
286
- }
287
- });
288
- };
289
- const encoderStream = createAudioDataStream(encoderResultStream);
290
- const previewStream = createAudioDataStream(previewResultStream);
291
- await encoderChannel.sendStream(encoderStream, streamMetadata);
292
- this.channel.sendStream(previewStream, streamMetadata);
293
- this.encoderStreamAttached = true;
294
- await this.scheduleMix();
295
- }
296
- async bufferTrackStream(trackId, stream, metadata) {
297
- const buffer = this.trackBuffers.get(trackId);
298
- if (!buffer) {
299
- await stream.cancel();
300
- return;
301
- }
302
- const reader = stream.getReader();
303
- const process = async () => {
304
- while (true) {
305
- if (buffer.queue.length >= this.maxQueuedSegments) {
306
- await this.waitForQueueSpace(trackId);
307
- }
308
- const { done, value } = await reader.read();
309
- if (done) {
310
- buffer.ended = true;
311
- reader.releaseLock();
312
- return;
313
- }
314
- buffer.queue.push({
315
- audioData: value,
316
- timestampUs: value.timestamp ?? 0,
317
- durationUs: value.duration ?? Math.round(value.numberOfFrames / (metadata.sampleRate ?? 48e3) * 1e6)
318
- });
319
- this.scheduleMix();
320
- }
321
- };
322
- process().catch((error) => {
323
- buffer.ended = true;
324
- reader.releaseLock();
325
- console.error("[AudioComposeWorker] Track stream error:", error);
326
- });
327
- }
328
- async scheduleMix() {
329
- if (this.mixing || !this.mixStream || !this.encoderStreamAttached || !this.mixer) {
330
- return;
331
- }
332
- const window = this.computeNextWindow();
333
- if (!window) {
334
- return;
335
- }
336
- this.mixing = true;
337
- try {
338
- const request = this.buildMixRequest(window.timeUs, window.durationUs);
339
- if (!request) {
340
- return;
341
- }
342
- const writer = this.mixStream.writable.getWriter();
343
- try {
344
- await writer.write(request);
345
- } finally {
346
- writer.releaseLock();
347
- }
348
- } catch (error) {
349
- console.error("[AudioComposeWorker] Failed to enqueue mix request:", error);
350
- } finally {
351
- this.mixing = false;
352
- if (this.hasBufferedAudio()) {
353
- queueMicrotask(() => {
354
- void this.scheduleMix();
355
- });
356
- }
357
- }
358
- }
359
- computeNextWindow() {
360
- let earliest = null;
361
- for (const buffer of this.trackBuffers.values()) {
362
- if (buffer.queue.length === 0) {
363
- continue;
364
- }
365
- const firstFrame = buffer.queue[0];
366
- if (!firstFrame) {
367
- continue;
368
- }
369
- const ts = firstFrame.timestampUs;
370
- if (earliest === null || ts < earliest) {
371
- earliest = ts;
372
- }
373
- }
374
- if (earliest === null) {
375
- return null;
376
- }
377
- return {
378
- timeUs: earliest,
379
- durationUs: this.mixWindowUs
380
- };
381
- }
382
- buildMixRequest(timeUs, _durationUs) {
383
- if (!this.mixer) {
384
- return null;
385
- }
386
- const tracks = [];
387
- let resolvedDurationUs = this.mixWindowUs;
388
- for (const [trackId, buffer] of this.trackBuffers.entries()) {
389
- const segment = this.consumeSegment(trackId, buffer);
390
- if (!segment) {
391
- continue;
392
- }
393
- tracks.push({
394
- trackId,
395
- clipId: buffer.clipId,
396
- audioData: segment.audioData,
397
- config: segment.config,
398
- type: buffer.type,
399
- sampleRate: this.mixer.config.sampleRate,
400
- numberOfChannels: this.mixer.config.numberOfChannels
401
- });
402
- resolvedDurationUs = Math.min(resolvedDurationUs, segment.durationUs ?? this.mixWindowUs);
403
- }
404
- if (tracks.length === 0) {
405
- return null;
406
- }
407
- return {
408
- tracks,
409
- timeUs,
410
- durationUs: resolvedDurationUs
411
- };
412
- }
413
- consumeSegment(trackId, buffer) {
414
- if (buffer.queue.length === 0) {
415
- return null;
416
- }
417
- const head = buffer.queue[0];
418
- if (!head) {
419
- return null;
420
- }
421
- const queueItem = buffer.queue.shift();
422
- if (!queueItem) {
423
- return null;
424
- }
425
- this.resolveQueueWaiter(trackId);
426
- return {
427
- audioData: queueItem.audioData,
428
- config: buffer.config,
429
- sampleRate: this.mixer.config.sampleRate,
430
- numberOfChannels: this.mixer.config.numberOfChannels,
431
- durationUs: queueItem.durationUs
432
- };
433
- }
434
- hasBufferedAudio() {
435
- for (const buffer of this.trackBuffers.values()) {
436
- if (buffer.queue.length > 0) {
437
- return true;
438
- }
439
- }
440
- return false;
441
- }
442
- cloneTrackConfig(config) {
443
- return {
444
- startTimeUs: config.startTimeUs,
445
- durationUs: config.durationUs,
446
- volume: config.volume,
447
- fadeIn: config.fadeIn ? { ...config.fadeIn } : void 0,
448
- fadeOut: config.fadeOut ? { ...config.fadeOut } : void 0,
449
- effects: config.effects ? config.effects.map((effect) => ({
450
- type: effect.type,
451
- params: effect.params ?? {}
452
- })) : void 0,
453
- duckingTag: config.duckingTag
454
- };
455
- }
456
- applyTrackConfigPatch(target, patch) {
457
- if (patch.fadeIn) {
458
- target.fadeIn = { ...patch.fadeIn };
459
- } else if (patch.fadeIn === null) {
460
- target.fadeIn = void 0;
461
- }
462
- if (patch.fadeOut) {
463
- target.fadeOut = { ...patch.fadeOut };
464
- } else if (patch.fadeOut === null) {
465
- target.fadeOut = void 0;
466
- }
467
- if (patch.effects) {
468
- target.effects = patch.effects.map((effect) => ({
469
- type: effect.type,
470
- params: effect.params ?? {}
471
- }));
472
- }
473
- if (typeof patch.volume === "number") {
474
- target.volume = patch.volume;
475
- }
476
- if (patch.startTimeUs !== void 0) {
477
- target.startTimeUs = patch.startTimeUs;
478
- }
479
- if (patch.durationUs !== void 0) {
480
- target.durationUs = patch.durationUs;
481
- }
482
- if (patch.duckingTag !== void 0) {
483
- target.duckingTag = patch.duckingTag;
484
- }
485
- }
486
- async waitForQueueSpace(trackId) {
487
- const existing = this.trackQueueWaiters.get(trackId);
488
- if (existing && existing.length === 0) {
489
- this.trackQueueWaiters.delete(trackId);
490
- }
491
- await new Promise((resolve) => {
492
- const waiters = this.trackQueueWaiters.get(trackId);
493
- if (waiters) {
494
- waiters.push(resolve);
495
- } else {
496
- this.trackQueueWaiters.set(trackId, [resolve]);
497
- }
498
- });
499
- }
500
- resolveQueueWaiter(trackId) {
501
- const waiters = this.trackQueueWaiters.get(trackId);
502
- if (!waiters || waiters.length === 0) {
503
- return;
504
- }
505
- const resolve = waiters.shift();
506
- resolve?.();
507
- if (!waiters.length) {
508
- this.trackQueueWaiters.delete(trackId);
509
- }
510
- }
511
- disposeTrackBuffer(trackId) {
512
- const buffer = this.trackBuffers.get(trackId);
513
- if (!buffer) {
514
- return;
515
- }
516
- for (const item of buffer.queue) {
517
- try {
518
- item.audioData.close();
519
- } catch (error) {
520
- console.warn("[AudioComposeWorker] Failed to close AudioData on disposal", error);
521
- }
522
- }
523
- this.trackBuffers.delete(trackId);
524
- const waiters = this.trackQueueWaiters.get(trackId);
525
- if (waiters) {
526
- waiters.forEach((resolve) => resolve());
527
- this.trackQueueWaiters.delete(trackId);
528
- }
529
- }
530
- }
531
- const worker = new AudioComposeWorker();
532
- self.addEventListener("beforeunload", () => {
533
- worker["handleDispose"]();
534
- });
535
- const audioCompose_worker = null;
536
- export {
537
- AudioComposeWorker,
538
- audioCompose_worker as default
539
- };
540
- //# sourceMappingURL=audio-compose.worker.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"audio-compose.worker.js","sources":["../../../src/stages/compose/audio-compose.worker.ts"],"sourcesContent":["import { WorkerChannel } from '../../worker/WorkerChannel';\nimport { WorkerMessageType, WorkerState } from '../../worker/types';\nimport { AudioMixer } from './AudioMixer';\nimport { AudioDucker } from './AudioDucker';\nimport {\n AudioComposeConfig,\n MixRequest,\n MixResult,\n DuckingConfig,\n AudioTrack,\n AudioTrackConfig,\n} from './types';\nimport type { TimeUs } from '../../model/types';\n\ninterface TrackBufferItem {\n audioData: AudioData;\n timestampUs: TimeUs;\n durationUs: TimeUs;\n}\n\ninterface TrackBuffer {\n clipId: string;\n queue: TrackBufferItem[];\n ended: boolean;\n config: AudioTrackConfig;\n type: AudioTrack['type'];\n}\n\n/**\n * AudioComposeWorker - Audio mixing and ducking stage\n * Mixes multiple audio tracks with volume, ducking, and effects\n *\n * Pipeline: DecodeWorker → AudioComposeWorker → EncodeWorker\n *\n * Features:\n * - Multi-track PCM mixing\n * - BGM ducking for voice tracks\n * - Stream-based processing with backpressure\n * - Real-time volume and effect adjustments\n */\nexport class AudioComposeWorker {\n private channel: WorkerChannel;\n private mixer: AudioMixer | null = null;\n private ducker: AudioDucker | null = null;\n private mixStream: TransformStream<MixRequest, MixResult> | null = null;\n\n // Connections to other workers\n private decoderPort: MessagePort | null = null;\n private encoderPort: MessagePort | null = null;\n // Track buffer map\n private trackBuffers = new Map<string, TrackBuffer>();\n private trackQueueWaiters = new Map<string, Array<() => void>>();\n private encoderStreamAttached = false;\n private readonly mixWindowUs: TimeUs = 40_000; // 40ms window\n private readonly maxQueuedSegments = 8;\n private mixing = false;\n\n constructor() {\n // Initialize WorkerChannel\n this.channel = new WorkerChannel(self as any, {\n name: 'AudioComposeWorker',\n timeout: 30000,\n });\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n // Register message handlers\n this.channel.registerHandler('configure', this.handleConfigure.bind(this));\n // Unified stream connect\n this.channel.registerHandler('connect' as any, this.handleConnect.bind(this));\n this.channel.registerHandler('add_track', this.handleAddTrack.bind(this));\n this.channel.registerHandler('remove_track', this.handleRemoveTrack.bind(this));\n this.channel.registerHandler('update_track', this.handleUpdateTrack.bind(this));\n this.channel.registerHandler('configure_ducking', this.handleConfigureDucking.bind(this));\n this.channel.registerHandler('get_stats', this.handleGetStats.bind(this));\n this.channel.registerHandler(WorkerMessageType.Dispose, this.handleDispose.bind(this));\n }\n\n /** Unified connect handler mapping for stream pipeline */\n private async handleConnect(payload: {\n direction: 'upstream' | 'downstream';\n port: MessagePort;\n streamType?: string;\n }): Promise<{ success: boolean }> {\n if (payload.direction === 'upstream') {\n this.decoderPort = payload.port;\n const decoderChannel = new WorkerChannel(this.decoderPort, {\n name: 'AudioCompose-Decoder',\n timeout: 30000,\n });\n\n decoderChannel.registerHandler('audio_track:add', this.handleAddTrack.bind(this));\n decoderChannel.registerHandler('audio_track:remove', this.handleRemoveTrack.bind(this));\n decoderChannel.registerHandler('audio_track:update', this.handleUpdateTrack.bind(this));\n\n decoderChannel.receiveStream(this.handleReceiveStream.bind(this));\n return { success: true };\n }\n\n if (payload.direction === 'downstream') {\n // if (payload.streamType === 'preview') {\n // this.previewPort = payload.port;\n // this.previewChannel?.dispose?.();\n // this.previewChannel = new WorkerChannel(this.previewPort, {\n // name: 'AudioCompose-Preview',\n // timeout: 30000,\n // });\n // return { success: true };\n // }\n\n this.encoderPort = payload.port;\n return { success: true };\n }\n\n return { success: true };\n }\n\n private async handleReceiveStream(\n stream: ReadableStream,\n metadata?: Record<string, any>\n ): Promise<void> {\n if (metadata?.streamType !== 'audio' || !this.mixStream || !this.mixer) {\n return;\n }\n\n const update: Partial<AudioComposeConfig> = {};\n const currentConfig = this.mixer.getConfig();\n\n if (typeof metadata?.sampleRate === 'number' && metadata.sampleRate > 0) {\n if (!currentConfig.sampleRate || currentConfig.sampleRate !== metadata.sampleRate) {\n update.sampleRate = metadata.sampleRate;\n }\n }\n\n if (typeof metadata?.numberOfChannels === 'number' && metadata.numberOfChannels > 0) {\n if (\n !currentConfig.numberOfChannels ||\n metadata.numberOfChannels > currentConfig.numberOfChannels\n ) {\n update.numberOfChannels = metadata.numberOfChannels;\n }\n }\n\n if (Object.keys(update).length > 0) {\n this.mixer.updateConfig(update);\n }\n\n const mixerConfig = this.mixer.getConfig();\n\n const streamMetadata = {\n ...metadata,\n streamType: 'audio',\n sampleRate: mixerConfig.sampleRate,\n numberOfChannels: mixerConfig.numberOfChannels,\n };\n\n await this.attachEncodeStream(streamMetadata);\n const trackId = (metadata as any)?.trackId ?? (metadata as any)?.clipId;\n if (!trackId) {\n console.warn('[AudioComposeWorker] Missing track identifier in audio stream metadata');\n await stream.cancel();\n return;\n }\n\n await this.bufferTrackStream(trackId, stream as ReadableStream<AudioData>, streamMetadata);\n this.scheduleMix();\n }\n\n /**\n * Configure audio composer\n * @param payload.config - Audio composition configuration\n * @param payload.initial - If true, initialize worker state; otherwise just update config\n */\n private async handleConfigure(payload: {\n config: AudioComposeConfig;\n initial?: boolean;\n }): Promise<{ success: boolean }> {\n const { config, initial = false } = payload;\n\n try {\n if (initial) {\n // Initial setup - set worker state to ready\n this.channel.state = WorkerState.Ready;\n\n // Create new mixer and ducker instances\n // Previous instances will be garbage collected\n\n this.mixer = new AudioMixer(config);\n this.ducker = new AudioDucker(config.sampleRate);\n\n // Create stream with ducking support\n this.mixStream = this.mixer.createMixStream(this.ducker);\n\n // Notify configuration complete\n this.channel.notify('configured', {\n sampleRate: config.sampleRate,\n numberOfChannels: config.numberOfChannels,\n });\n } else {\n // Update configuration only (e.g., backpressure settings)\n if (!this.mixer || !this.ducker) {\n throw {\n code: 'NOT_INITIALIZED',\n message: 'Audio composer not initialized. Call configure with initial=true first',\n };\n }\n\n // AudioMixer doesn't support runtime config updates\n // Would need initial=true for changes\n }\n\n return { success: true };\n } catch (error: any) {\n throw {\n code: error.code || 'CONFIG_ERROR',\n message: error.message,\n };\n }\n }\n\n /**\n * Connect to decoder worker to receive audio streams\n */\n /**\n * Add an audio track\n */\n private handleAddTrack(payload: {\n clipId: string;\n trackId: string;\n config: AudioTrackConfig;\n type?: AudioTrack['type'];\n }): { success: boolean } {\n if (!this.mixer) {\n throw {\n code: 'NOT_CONFIGURED',\n message: 'Mixer not configured',\n };\n }\n\n const config = this.cloneTrackConfig(payload.config);\n const track: AudioTrack = {\n id: payload.trackId,\n clipId: payload.clipId,\n type: payload.type ?? 'other',\n config,\n };\n\n this.mixer.addTrack(track);\n this.trackBuffers.set(payload.trackId, {\n clipId: payload.clipId,\n queue: [],\n ended: false,\n config,\n type: payload.type ?? 'other',\n });\n\n this.channel.notify('track_added', {\n trackId: track.id,\n trackType: track.type,\n });\n\n return { success: true };\n }\n\n /**\n * Remove an audio track\n */\n private handleRemoveTrack(payload: { clipId?: string; trackId: string }): { success: boolean } {\n if (!this.mixer) {\n throw {\n code: 'NOT_CONFIGURED',\n message: 'Mixer not configured',\n };\n }\n\n this.mixer.removeTrack(payload.trackId);\n this.disposeTrackBuffer(payload.trackId);\n\n this.channel.notify('track_removed', {\n trackId: payload.trackId,\n });\n\n return { success: true };\n }\n\n /**\n * Update track configuration\n */\n private handleUpdateTrack(payload: {\n trackId: string;\n config: Partial<AudioTrackConfig> & { type?: AudioTrack['type'] };\n }): {\n success: boolean;\n } {\n if (!this.mixer) {\n throw {\n code: 'NOT_CONFIGURED',\n message: 'Mixer not configured',\n };\n }\n\n this.mixer.updateTrack(payload.trackId, payload.config);\n const buffer = this.trackBuffers.get(payload.trackId);\n if (buffer) {\n if (payload.config.type) {\n buffer.type = payload.config.type;\n }\n this.applyTrackConfigPatch(buffer.config, payload.config);\n }\n\n this.channel.notify('track_updated', {\n trackId: payload.trackId,\n });\n\n return { success: true };\n }\n\n /**\n * Configure audio ducking\n */\n private handleConfigureDucking(config: DuckingConfig): { success: boolean } {\n if (!this.ducker) {\n throw {\n code: 'NOT_CONFIGURED',\n message: 'Ducker not configured',\n };\n }\n\n this.ducker.configure(config);\n\n this.channel.notify('ducking_configured', {\n enabled: config.enabled,\n });\n\n return { success: true };\n }\n\n /**\n * Get mixer statistics\n */\n private async handleGetStats(): Promise<{\n tracks?: any[];\n ducking?: any;\n state?: WorkerState;\n }> {\n if (!this.mixer) {\n return { state: this.channel.state };\n }\n\n return {\n tracks: this.mixer.tracks,\n ducking: this.ducker\n ? {\n configured: this.ducker !== null,\n }\n : null,\n state: this.channel.state,\n };\n }\n\n /**\n * Dispose worker and cleanup resources\n */\n private async handleDispose(): Promise<{ success: boolean }> {\n // Clean up references\n this.mixer = null;\n this.ducker = null;\n this.mixStream = null;\n\n // Close connections\n this.decoderPort?.close();\n this.decoderPort = null;\n this.encoderPort?.close();\n this.encoderPort = null;\n\n this.channel.state = WorkerState.Disposed;\n\n return { success: true };\n }\n\n private async attachEncodeStream(metadata: Record<string, any>): Promise<void> {\n if (!this.mixStream || !this.encoderPort || this.encoderStreamAttached || !this.mixer) {\n return;\n }\n\n const encoderChannel = new WorkerChannel(this.encoderPort, {\n name: 'AudioCompose-Encoder',\n timeout: 30000,\n });\n\n const mixerConfig = this.mixer?.getConfig();\n\n const streamMetadata = {\n ...metadata,\n streamType: 'audio',\n sampleRate: mixerConfig?.sampleRate ?? metadata?.sampleRate,\n numberOfChannels: mixerConfig?.numberOfChannels ?? metadata?.numberOfChannels,\n };\n\n const [encoderResultStream, previewResultStream] = this.mixStream.readable.tee();\n\n const createAudioDataStream = (\n source: ReadableStream<MixResult>\n ): ReadableStream<AudioData> => {\n return new ReadableStream<AudioData>({\n start: (controller) => {\n const reader = source.getReader();\n\n const pump = async (): Promise<void> => {\n const { done, value } = await reader.read();\n if (done) {\n reader.releaseLock();\n controller.close();\n return;\n }\n\n try {\n controller.enqueue(value.audioData);\n } catch (error) {\n controller.error(error);\n reader.releaseLock();\n return;\n }\n\n await pump();\n };\n\n pump().catch((error) => {\n reader.releaseLock();\n controller.error(error);\n });\n },\n });\n };\n\n const encoderStream = createAudioDataStream(encoderResultStream);\n const previewStream = createAudioDataStream(previewResultStream);\n\n await encoderChannel.sendStream(encoderStream, streamMetadata);\n\n this.channel.sendStream(previewStream, streamMetadata);\n\n this.encoderStreamAttached = true;\n await this.scheduleMix();\n }\n\n private async bufferTrackStream(\n trackId: string,\n stream: ReadableStream<AudioData>,\n metadata: Record<string, any>\n ): Promise<void> {\n const buffer = this.trackBuffers.get(trackId);\n if (!buffer) {\n await stream.cancel();\n return;\n }\n\n const reader = stream.getReader();\n const process = async (): Promise<void> => {\n while (true) {\n if (buffer.queue.length >= this.maxQueuedSegments) {\n await this.waitForQueueSpace(trackId);\n }\n\n const { done, value } = await reader.read();\n if (done) {\n buffer.ended = true;\n reader.releaseLock();\n return;\n }\n\n buffer.queue.push({\n audioData: value,\n timestampUs: value.timestamp ?? 0,\n durationUs:\n value.duration ??\n Math.round((value.numberOfFrames / (metadata.sampleRate ?? 48_000)) * 1_000_000),\n });\n\n // mix scheduling is triggered after queue updates via scheduleMix()\n this.scheduleMix();\n }\n };\n\n process().catch((error) => {\n buffer.ended = true;\n reader.releaseLock();\n console.error('[AudioComposeWorker] Track stream error:', error);\n });\n }\n\n private async scheduleMix(): Promise<void> {\n if (this.mixing || !this.mixStream || !this.encoderStreamAttached || !this.mixer) {\n return;\n }\n\n const window = this.computeNextWindow();\n if (!window) {\n return;\n }\n\n this.mixing = true;\n try {\n const request = this.buildMixRequest(window.timeUs, window.durationUs);\n if (!request) {\n return;\n }\n\n const writer = this.mixStream.writable.getWriter();\n try {\n await writer.write(request);\n } finally {\n writer.releaseLock();\n }\n } catch (error) {\n console.error('[AudioComposeWorker] Failed to enqueue mix request:', error);\n } finally {\n this.mixing = false;\n if (this.hasBufferedAudio()) {\n queueMicrotask(() => {\n void this.scheduleMix();\n });\n }\n }\n }\n\n private computeNextWindow(): { timeUs: TimeUs; durationUs: TimeUs } | null {\n let earliest: TimeUs | null = null;\n\n for (const buffer of this.trackBuffers.values()) {\n if (buffer.queue.length === 0) {\n continue;\n }\n const firstFrame = buffer.queue[0];\n if (!firstFrame) {\n continue;\n }\n const ts = firstFrame.timestampUs;\n if (earliest === null || ts < earliest) {\n earliest = ts;\n }\n }\n\n if (earliest === null) {\n return null;\n }\n\n return {\n timeUs: earliest,\n durationUs: this.mixWindowUs,\n };\n }\n\n private buildMixRequest(timeUs: TimeUs, _durationUs: TimeUs): MixRequest | null {\n if (!this.mixer) {\n return null;\n }\n\n const tracks: MixRequest['tracks'] = [];\n let resolvedDurationUs = this.mixWindowUs;\n\n for (const [trackId, buffer] of this.trackBuffers.entries()) {\n const segment = this.consumeSegment(trackId, buffer);\n if (!segment) {\n continue;\n }\n\n tracks.push({\n trackId,\n clipId: buffer.clipId,\n audioData: segment.audioData,\n config: segment.config,\n type: buffer.type,\n sampleRate: this.mixer.config.sampleRate,\n numberOfChannels: this.mixer.config.numberOfChannels,\n });\n\n resolvedDurationUs = Math.min(resolvedDurationUs, segment.durationUs ?? this.mixWindowUs);\n }\n\n if (tracks.length === 0) {\n return null;\n }\n\n return {\n tracks,\n timeUs,\n durationUs: resolvedDurationUs,\n };\n }\n\n private consumeSegment(\n trackId: string,\n buffer: TrackBuffer\n ): {\n audioData: AudioData;\n config: AudioTrackConfig;\n sampleRate: number;\n numberOfChannels: number;\n durationUs?: TimeUs;\n } | null {\n if (buffer.queue.length === 0) {\n return null;\n }\n\n const head = buffer.queue[0];\n if (!head) {\n return null;\n }\n\n const queueItem = buffer.queue.shift();\n if (!queueItem) {\n return null;\n }\n\n this.resolveQueueWaiter(trackId);\n\n return {\n audioData: queueItem.audioData,\n config: buffer.config,\n sampleRate: this.mixer!.config.sampleRate,\n numberOfChannels: this.mixer!.config.numberOfChannels,\n durationUs: queueItem.durationUs,\n };\n }\n\n private hasBufferedAudio(): boolean {\n for (const buffer of this.trackBuffers.values()) {\n if (buffer.queue.length > 0) {\n return true;\n }\n }\n return false;\n }\n\n private cloneTrackConfig(config: AudioTrackConfig): AudioTrackConfig {\n return {\n startTimeUs: config.startTimeUs,\n durationUs: config.durationUs,\n volume: config.volume,\n fadeIn: config.fadeIn ? { ...config.fadeIn } : undefined,\n fadeOut: config.fadeOut ? { ...config.fadeOut } : undefined,\n effects: config.effects\n ? config.effects.map((effect) => ({\n type: effect.type,\n params: effect.params ?? {},\n }))\n : undefined,\n duckingTag: config.duckingTag,\n };\n }\n\n private applyTrackConfigPatch(target: AudioTrackConfig, patch: Partial<AudioTrackConfig>): void {\n if (patch.fadeIn) {\n target.fadeIn = { ...patch.fadeIn };\n } else if (patch.fadeIn === null) {\n target.fadeIn = undefined;\n }\n\n if (patch.fadeOut) {\n target.fadeOut = { ...patch.fadeOut };\n } else if (patch.fadeOut === null) {\n target.fadeOut = undefined;\n }\n\n if (patch.effects) {\n target.effects = patch.effects.map((effect) => ({\n type: effect.type,\n params: effect.params ?? {},\n }));\n }\n\n if (typeof patch.volume === 'number') {\n target.volume = patch.volume;\n }\n\n if (patch.startTimeUs !== undefined) {\n target.startTimeUs = patch.startTimeUs;\n }\n\n if (patch.durationUs !== undefined) {\n target.durationUs = patch.durationUs;\n }\n\n if (patch.duckingTag !== undefined) {\n target.duckingTag = patch.duckingTag;\n }\n }\n\n private async waitForQueueSpace(trackId: string): Promise<void> {\n const existing = this.trackQueueWaiters.get(trackId);\n if (existing && existing.length === 0) {\n this.trackQueueWaiters.delete(trackId);\n }\n\n await new Promise<void>((resolve) => {\n const waiters = this.trackQueueWaiters.get(trackId);\n if (waiters) {\n waiters.push(resolve);\n } else {\n this.trackQueueWaiters.set(trackId, [resolve]);\n }\n });\n }\n\n private resolveQueueWaiter(trackId: string): void {\n const waiters = this.trackQueueWaiters.get(trackId);\n if (!waiters || waiters.length === 0) {\n return;\n }\n\n const resolve = waiters.shift();\n resolve?.();\n if (!waiters.length) {\n this.trackQueueWaiters.delete(trackId);\n }\n }\n\n private disposeTrackBuffer(trackId: string): void {\n const buffer = this.trackBuffers.get(trackId);\n if (!buffer) {\n return;\n }\n\n for (const item of buffer.queue) {\n try {\n item.audioData.close();\n } catch (error) {\n console.warn('[AudioComposeWorker] Failed to close AudioData on disposal', error);\n }\n }\n\n this.trackBuffers.delete(trackId);\n\n const waiters = this.trackQueueWaiters.get(trackId);\n if (waiters) {\n waiters.forEach((resolve) => resolve());\n this.trackQueueWaiters.delete(trackId);\n }\n }\n}\n\n// Initialize worker\nconst worker = new AudioComposeWorker();\n\n// Handle worker termination\nself.addEventListener('beforeunload', () => {\n worker['handleDispose']();\n});\n\nexport default null; // Required for TypeScript worker compilation\n"],"names":[],"mappings":"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"}