@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.
- package/dist/Meframe.d.ts.map +1 -1
- package/dist/Meframe.js +6 -4
- package/dist/Meframe.js.map +1 -1
- package/dist/cache/CacheManager.d.ts +2 -2
- package/dist/cache/CacheManager.d.ts.map +1 -1
- package/dist/cache/CacheManager.js +4 -3
- package/dist/cache/CacheManager.js.map +1 -1
- package/dist/cache/l1/VideoL1Cache.d.ts +2 -2
- package/dist/cache/l1/VideoL1Cache.d.ts.map +1 -1
- package/dist/cache/l1/VideoL1Cache.js +13 -8
- package/dist/cache/l1/VideoL1Cache.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +3 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/types.d.ts +6 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.d.ts +7 -8
- package/dist/controllers/PlaybackController.d.ts.map +1 -1
- package/dist/controllers/PlaybackController.js +56 -76
- package/dist/controllers/PlaybackController.js.map +1 -1
- package/dist/controllers/types.d.ts +2 -3
- package/dist/controllers/types.d.ts.map +1 -1
- package/dist/event/events.d.ts +1 -4
- package/dist/event/events.d.ts.map +1 -1
- package/dist/event/events.js.map +1 -1
- package/dist/model/CompositionModel.d.ts +1 -0
- package/dist/model/CompositionModel.d.ts.map +1 -1
- package/dist/model/CompositionModel.js +2 -0
- package/dist/model/CompositionModel.js.map +1 -1
- package/dist/model/patch.d.ts +6 -2
- package/dist/model/patch.d.ts.map +1 -1
- package/dist/model/patch.js +76 -2
- package/dist/model/patch.js.map +1 -1
- package/dist/model/types.d.ts +1 -0
- package/dist/model/types.d.ts.map +1 -1
- package/dist/orchestrator/CompositionPlanner.d.ts +8 -7
- package/dist/orchestrator/CompositionPlanner.d.ts.map +1 -1
- package/dist/orchestrator/CompositionPlanner.js +33 -56
- package/dist/orchestrator/CompositionPlanner.js.map +1 -1
- package/dist/orchestrator/Orchestrator.d.ts +0 -1
- package/dist/orchestrator/Orchestrator.d.ts.map +1 -1
- package/dist/orchestrator/Orchestrator.js +40 -19
- package/dist/orchestrator/Orchestrator.js.map +1 -1
- package/dist/orchestrator/VideoClipSession.d.ts +3 -5
- package/dist/orchestrator/VideoClipSession.d.ts.map +1 -1
- package/dist/orchestrator/VideoClipSession.js +66 -69
- package/dist/orchestrator/VideoClipSession.js.map +1 -1
- package/dist/orchestrator/types.d.ts +2 -0
- package/dist/orchestrator/types.d.ts.map +1 -1
- package/dist/stages/compose/GlobalAudioSession.d.ts +6 -0
- package/dist/stages/compose/GlobalAudioSession.d.ts.map +1 -1
- package/dist/stages/compose/GlobalAudioSession.js +17 -1
- package/dist/stages/compose/GlobalAudioSession.js.map +1 -1
- package/dist/stages/compose/types.d.ts +2 -1
- package/dist/stages/compose/types.d.ts.map +1 -1
- package/dist/stages/demux/MP4Demuxer.d.ts +0 -1
- package/dist/stages/demux/MP4Demuxer.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.d.ts +22 -1
- package/dist/stages/load/ResourceLoader.d.ts.map +1 -1
- package/dist/stages/load/ResourceLoader.js +71 -25
- package/dist/stages/load/ResourceLoader.js.map +1 -1
- package/dist/stages/load/TaskManager.d.ts +1 -1
- package/dist/stages/load/TaskManager.d.ts.map +1 -1
- package/dist/stages/load/TaskManager.js +3 -2
- package/dist/stages/load/TaskManager.js.map +1 -1
- package/dist/stages/load/types.d.ts +2 -0
- package/dist/stages/load/types.d.ts.map +1 -1
- package/dist/utils/time-utils.d.ts +3 -2
- package/dist/utils/time-utils.d.ts.map +1 -1
- package/dist/utils/time-utils.js +2 -1
- package/dist/utils/time-utils.js.map +1 -1
- package/dist/vite-plugin.d.ts +5 -3
- package/dist/vite-plugin.d.ts.map +1 -1
- package/dist/vite-plugin.js +109 -52
- package/dist/vite-plugin.js.map +1 -1
- package/dist/worker/WorkerPool.d.ts +9 -0
- package/dist/worker/WorkerPool.d.ts.map +1 -1
- package/dist/worker/WorkerPool.js +32 -5
- package/dist/worker/WorkerPool.js.map +1 -1
- package/dist/{stages/demux → workers}/MP4Demuxer.js +4 -13
- package/dist/workers/MP4Demuxer.js.map +1 -0
- package/dist/workers/WorkerChannel.js +486 -0
- package/dist/workers/WorkerChannel.js.map +1 -0
- package/dist/{assets/video-demux.worker-D019I7GQ.js → workers/mp4box.all.js} +4 -912
- package/dist/workers/mp4box.all.js.map +1 -0
- package/dist/{assets/audio-compose.worker-nGVvHD5Q.js → workers/stages/compose/audio-compose.worker.js} +7 -481
- package/dist/workers/stages/compose/audio-compose.worker.js.map +1 -0
- package/dist/{assets/video-compose.worker-DPzsC21d.js → workers/stages/compose/video-compose.worker.js} +120 -562
- package/dist/workers/stages/compose/video-compose.worker.js.map +1 -0
- package/dist/{assets/decode.worker-DpWHsc7R.js → workers/stages/decode/decode.worker.js} +7 -481
- package/dist/workers/stages/decode/decode.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/demux/audio-demux.worker.js +184 -4
- package/dist/workers/stages/demux/audio-demux.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/demux/video-demux.worker.js +2 -3
- package/dist/workers/stages/demux/video-demux.worker.js.map +1 -0
- package/dist/{stages → workers/stages}/encode/encode.worker.js +238 -4
- package/dist/workers/stages/encode/encode.worker.js.map +1 -0
- package/dist/{stages/mux/MP4Muxer.js → workers/stages/mux/mux.worker.js} +244 -5
- package/dist/workers/stages/mux/mux.worker.js.map +1 -0
- package/package.json +21 -21
- package/dist/assets/audio-compose.worker-nGVvHD5Q.js.map +0 -1
- package/dist/assets/audio-demux.worker-xwWBtbAe.js +0 -8299
- package/dist/assets/audio-demux.worker-xwWBtbAe.js.map +0 -1
- package/dist/assets/decode.worker-DpWHsc7R.js.map +0 -1
- package/dist/assets/encode.worker-nfOb3kw6.js +0 -1026
- package/dist/assets/encode.worker-nfOb3kw6.js.map +0 -1
- package/dist/assets/mux.worker-uEMQY066.js +0 -8019
- package/dist/assets/mux.worker-uEMQY066.js.map +0 -1
- package/dist/assets/video-compose.worker-DPzsC21d.js.map +0 -1
- package/dist/assets/video-demux.worker-D019I7GQ.js.map +0 -1
- package/dist/controllers/PreviewHandle.d.ts +0 -25
- package/dist/controllers/PreviewHandle.d.ts.map +0 -1
- package/dist/controllers/PreviewHandle.js +0 -45
- package/dist/controllers/PreviewHandle.js.map +0 -1
- package/dist/model/dirty-range.js +0 -220
- package/dist/model/dirty-range.js.map +0 -1
- package/dist/model/types.js +0 -5
- package/dist/model/types.js.map +0 -1
- package/dist/plugins/BackpressureMonitor.js +0 -62
- package/dist/plugins/BackpressureMonitor.js.map +0 -1
- package/dist/stages/compose/AudioDucker.js +0 -161
- package/dist/stages/compose/AudioDucker.js.map +0 -1
- package/dist/stages/compose/AudioMixer.js +0 -373
- package/dist/stages/compose/AudioMixer.js.map +0 -1
- package/dist/stages/compose/FilterProcessor.js +0 -226
- package/dist/stages/compose/FilterProcessor.js.map +0 -1
- package/dist/stages/compose/LayerRenderer.js +0 -215
- package/dist/stages/compose/LayerRenderer.js.map +0 -1
- package/dist/stages/compose/TransitionProcessor.js +0 -189
- package/dist/stages/compose/TransitionProcessor.js.map +0 -1
- package/dist/stages/compose/VideoComposer.js +0 -186
- package/dist/stages/compose/VideoComposer.js.map +0 -1
- package/dist/stages/compose/audio-compose.worker.d.ts +0 -79
- package/dist/stages/compose/audio-compose.worker.d.ts.map +0 -1
- package/dist/stages/compose/audio-compose.worker.js +0 -540
- package/dist/stages/compose/audio-compose.worker.js.map +0 -1
- package/dist/stages/compose/audio-compose.worker2.js +0 -5
- package/dist/stages/compose/audio-compose.worker2.js.map +0 -1
- package/dist/stages/compose/video-compose.worker.d.ts +0 -60
- package/dist/stages/compose/video-compose.worker.d.ts.map +0 -1
- package/dist/stages/compose/video-compose.worker.js +0 -379
- package/dist/stages/compose/video-compose.worker.js.map +0 -1
- package/dist/stages/compose/video-compose.worker2.js +0 -5
- package/dist/stages/compose/video-compose.worker2.js.map +0 -1
- package/dist/stages/decode/AudioChunkDecoder.js +0 -82
- package/dist/stages/decode/AudioChunkDecoder.js.map +0 -1
- package/dist/stages/decode/BaseDecoder.js +0 -130
- package/dist/stages/decode/BaseDecoder.js.map +0 -1
- package/dist/stages/decode/VideoChunkDecoder.js +0 -199
- package/dist/stages/decode/VideoChunkDecoder.js.map +0 -1
- package/dist/stages/decode/decode.worker.d.ts +0 -70
- package/dist/stages/decode/decode.worker.d.ts.map +0 -1
- package/dist/stages/decode/decode.worker.js +0 -423
- package/dist/stages/decode/decode.worker.js.map +0 -1
- package/dist/stages/decode/decode.worker2.js +0 -5
- package/dist/stages/decode/decode.worker2.js.map +0 -1
- package/dist/stages/demux/MP3FrameParser.js +0 -186
- package/dist/stages/demux/MP3FrameParser.js.map +0 -1
- package/dist/stages/demux/MP4Demuxer.js.map +0 -1
- package/dist/stages/demux/audio-demux.worker.d.ts +0 -51
- package/dist/stages/demux/audio-demux.worker.d.ts.map +0 -1
- package/dist/stages/demux/audio-demux.worker.js.map +0 -1
- package/dist/stages/demux/audio-demux.worker2.js +0 -5
- package/dist/stages/demux/audio-demux.worker2.js.map +0 -1
- package/dist/stages/demux/video-demux.worker.d.ts +0 -51
- package/dist/stages/demux/video-demux.worker.d.ts.map +0 -1
- package/dist/stages/demux/video-demux.worker.js.map +0 -1
- package/dist/stages/demux/video-demux.worker2.js +0 -5
- package/dist/stages/demux/video-demux.worker2.js.map +0 -1
- package/dist/stages/encode/AudioChunkEncoder.js +0 -37
- package/dist/stages/encode/AudioChunkEncoder.js.map +0 -1
- package/dist/stages/encode/BaseEncoder.js +0 -164
- package/dist/stages/encode/BaseEncoder.js.map +0 -1
- package/dist/stages/encode/VideoChunkEncoder.js +0 -50
- package/dist/stages/encode/VideoChunkEncoder.js.map +0 -1
- package/dist/stages/encode/encode.worker.d.ts +0 -3
- package/dist/stages/encode/encode.worker.d.ts.map +0 -1
- package/dist/stages/encode/encode.worker.js.map +0 -1
- package/dist/stages/encode/encode.worker2.js +0 -5
- package/dist/stages/encode/encode.worker2.js.map +0 -1
- package/dist/stages/mux/MP4Muxer.js.map +0 -1
- package/dist/stages/mux/mux.worker.d.ts +0 -65
- package/dist/stages/mux/mux.worker.d.ts.map +0 -1
- package/dist/stages/mux/mux.worker.js +0 -219
- package/dist/stages/mux/mux.worker.js.map +0 -1
- package/dist/stages/mux/mux.worker2.js +0 -5
- package/dist/stages/mux/mux.worker2.js.map +0 -1
- package/dist/stages/mux/utils.js +0 -34
- package/dist/stages/mux/utils.js.map +0 -1
- package/dist/worker/worker-registry.d.ts +0 -12
- package/dist/worker/worker-registry.d.ts.map +0 -1
- package/dist/worker/worker-registry.js +0 -20
- 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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"}
|