@vunk/plus 1.10.2 → 1.10.3
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/components/WebGLRenderer.mjs +1 -1
- package/components/WebGPURenderer.mjs +1 -1
- package/components/agent-chat-provider/index.mjs +1 -1
- package/components/avatar/index.mjs +1 -1
- package/components/broadcasting-markdown/index.mjs +16 -13
- package/components/broadcasting-markdown/src/index.vue.d.ts +1 -75
- package/components/browserAll.mjs +1 -1
- package/components/bubble-list/index.mjs +3 -3
- package/components/bubble-templates/index.mjs +1 -1
- package/components/chat-independent/index.mjs +1 -1
- package/components/drawer/index.css +92 -92
- package/components/drawer/index.mjs +1 -1
- package/components/index2.mjs +90 -11
- package/components/lottie-animation/index.mjs +1 -1
- package/components/metahuman-broadcasting/index.d.ts +0 -1
- package/components/metahuman-broadcasting/index.mjs +3092 -68
- package/components/metahuman-broadcasting/src/index.vue.d.ts +75 -191
- package/components/monaco-editor/index.mjs +1 -1
- package/components/monaco-environment/index.mjs +1 -1
- package/components/pixi-frame/index.d.ts +4 -1
- package/components/pixi-frame/index.mjs +2 -2
- package/components/pixi-frame/src/bitmap-generator.vue.d.ts +146 -0
- package/components/recorder-button/index.mjs +1 -1
- package/components/routes-menu-content/index.mjs +3 -3
- package/components/scrollbar/index.mjs +3 -3
- package/components/sender/index.mjs +2 -2
- package/components/speech-button/index.mjs +3 -3
- package/components/table-columns/index.mjs +1 -1
- package/components/tables-v1/index.mjs +1 -1
- package/components/tables-v1-checkbox/index.mjs +1 -1
- package/components/text/index.mjs +1 -1
- package/components/typing-markdown/index.mjs +1 -1
- package/components/vditor/index.mjs +3 -3
- package/components/webworkerAll.mjs +1 -1
- package/components/xgplayer/index.mjs +2 -2
- package/icons/end/index.mjs +1 -1
- package/icons/keyboard/index.mjs +1 -1
- package/icons/start/index.mjs +1 -1
- package/icons/voice/index.mjs +1 -1
- package/index.css +92 -92
- package/package.json +4 -20
- package/shared/data/blobToAudioBuffer.d.ts +1 -0
- package/shared/data/imgUrlToBitmap.d.ts +1 -0
- package/shared/data/index.d.ts +2 -0
- package/shared/data/index.mjs +25 -0
- package/shared/StreamingInferenceService.mjs +0 -204
- package/shared/audioToFrames/index.d.ts +0 -19
- package/shared/audioToFrames/index.mjs +0 -44
- package/shared/audioToFrames/src/assets/mask.d.ts +0 -2
- package/shared/audioToFrames/src/audio/StreamingFeatureExtractorService.d.ts +0 -125
- package/shared/audioToFrames/src/audio/index.d.ts +0 -5
- package/shared/audioToFrames/src/audio/index.mjs +0 -375
- package/shared/audioToFrames/src/config.d.ts +0 -4
- package/shared/audioToFrames/src/core/constants.d.ts +0 -17
- package/shared/audioToFrames/src/core/index.d.ts +0 -6
- package/shared/audioToFrames/src/core/index.mjs +0 -24
- package/shared/audioToFrames/src/core/utils.d.ts +0 -26
- package/shared/audioToFrames/src/inference/StreamingInferenceService.d.ts +0 -189
- package/shared/audioToFrames/src/inference/index.d.ts +0 -5
- package/shared/audioToFrames/src/inference/index.mjs +0 -3
- package/shared/audioToFrames/src/media/DataLoaderService.d.ts +0 -59
- package/shared/audioToFrames/src/media/PredictiveFrameLoader.d.ts +0 -81
- package/shared/audioToFrames/src/media/index.d.ts +0 -6
- package/shared/audioToFrames/src/media/index.mjs +0 -236
- package/shared/config.mjs +0 -5
- package/shared/constants.mjs +0 -7
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
import { w as workerConfig } from '../../../config.mjs';
|
|
2
|
-
import { C as CHUNK_DURATION_SECONDS, a as NUM_SEQUENCE_FRAMES, N as NUM_MEL_BINS } from '../../../constants.mjs';
|
|
3
|
-
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
8
|
-
|
|
9
|
-
// src/lib/_AsyncQueueEntry.ts
|
|
10
|
-
var _AsyncQueueEntry = class _AsyncQueueEntry {
|
|
11
|
-
constructor(queue) {
|
|
12
|
-
__publicField(this, "promise");
|
|
13
|
-
__publicField(this, "resolve");
|
|
14
|
-
__publicField(this, "reject");
|
|
15
|
-
__publicField(this, "queue");
|
|
16
|
-
__publicField(this, "signal", null);
|
|
17
|
-
__publicField(this, "signalListener", null);
|
|
18
|
-
this.queue = queue;
|
|
19
|
-
this.promise = new Promise((resolve, reject) => {
|
|
20
|
-
this.resolve = resolve;
|
|
21
|
-
this.reject = reject;
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
setSignal(signal) {
|
|
25
|
-
if (signal.aborted) return this;
|
|
26
|
-
this.signal = signal;
|
|
27
|
-
this.signalListener = () => {
|
|
28
|
-
const index = this.queue["promises"].indexOf(this);
|
|
29
|
-
if (index !== -1) this.queue["promises"].splice(index, 1);
|
|
30
|
-
this.reject(new Error("Request aborted manually"));
|
|
31
|
-
};
|
|
32
|
-
this.signal.addEventListener("abort", this.signalListener);
|
|
33
|
-
return this;
|
|
34
|
-
}
|
|
35
|
-
use() {
|
|
36
|
-
this.dispose();
|
|
37
|
-
this.resolve();
|
|
38
|
-
return this;
|
|
39
|
-
}
|
|
40
|
-
abort() {
|
|
41
|
-
this.dispose();
|
|
42
|
-
this.reject(new Error("Request aborted manually"));
|
|
43
|
-
return this;
|
|
44
|
-
}
|
|
45
|
-
dispose() {
|
|
46
|
-
if (this.signal) {
|
|
47
|
-
this.signal.removeEventListener("abort", this.signalListener);
|
|
48
|
-
this.signal = null;
|
|
49
|
-
this.signalListener = null;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
__name(_AsyncQueueEntry, "AsyncQueueEntry");
|
|
54
|
-
var AsyncQueueEntry = _AsyncQueueEntry;
|
|
55
|
-
|
|
56
|
-
// src/lib/AsyncQueue.ts
|
|
57
|
-
var _AsyncQueue = class _AsyncQueue {
|
|
58
|
-
constructor() {
|
|
59
|
-
/**
|
|
60
|
-
* The promises array
|
|
61
|
-
*/
|
|
62
|
-
__publicField(this, "promises", []);
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* The amount of entries in the queue, including the head.
|
|
66
|
-
* @seealso {@link queued} for the queued count.
|
|
67
|
-
*/
|
|
68
|
-
get remaining() {
|
|
69
|
-
return this.promises.length;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* The amount of queued entries.
|
|
73
|
-
* @seealso {@link remaining} for the count with the head.
|
|
74
|
-
*/
|
|
75
|
-
get queued() {
|
|
76
|
-
return this.remaining === 0 ? 0 : this.remaining - 1;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Waits for last promise and queues a new one
|
|
80
|
-
* @example
|
|
81
|
-
* ```typescript
|
|
82
|
-
* const queue = new AsyncQueue();
|
|
83
|
-
* async function request(url, options) {
|
|
84
|
-
* await queue.wait({ signal: options.signal });
|
|
85
|
-
* try {
|
|
86
|
-
* const result = await fetch(url, options);
|
|
87
|
-
* // Do some operations with 'result'
|
|
88
|
-
* } finally {
|
|
89
|
-
* // Remove first entry from the queue and resolve for the next entry
|
|
90
|
-
* queue.shift();
|
|
91
|
-
* }
|
|
92
|
-
* }
|
|
93
|
-
*
|
|
94
|
-
* request(someUrl1, someOptions1); // Will call fetch() immediately
|
|
95
|
-
* request(someUrl2, someOptions2); // Will call fetch() after the first finished
|
|
96
|
-
* request(someUrl3, someOptions3); // Will call fetch() after the second finished
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
wait(options) {
|
|
100
|
-
const entry = new AsyncQueueEntry(this);
|
|
101
|
-
if (this.promises.length === 0) {
|
|
102
|
-
this.promises.push(entry);
|
|
103
|
-
return Promise.resolve();
|
|
104
|
-
}
|
|
105
|
-
this.promises.push(entry);
|
|
106
|
-
if (options?.signal) entry.setSignal(options.signal);
|
|
107
|
-
return entry.promise;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Unlocks the head lock and transfers the next lock (if any) to the head.
|
|
111
|
-
*/
|
|
112
|
-
shift() {
|
|
113
|
-
if (this.promises.length === 0) return;
|
|
114
|
-
if (this.promises.length === 1) {
|
|
115
|
-
this.promises.shift();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
this.promises.shift();
|
|
119
|
-
this.promises[0].use();
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Aborts all the pending promises.
|
|
123
|
-
* @note To avoid race conditions, this does **not** unlock the head lock.
|
|
124
|
-
*/
|
|
125
|
-
abortAll() {
|
|
126
|
-
if (this.queued === 0) return;
|
|
127
|
-
for (let i = 1; i < this.promises.length; ++i) {
|
|
128
|
-
this.promises[i].abort();
|
|
129
|
-
}
|
|
130
|
-
this.promises.length = 1;
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
__name(_AsyncQueue, "AsyncQueue");
|
|
134
|
-
var AsyncQueue = _AsyncQueue;
|
|
135
|
-
|
|
136
|
-
async function createAudioContext() {
|
|
137
|
-
const AudioContextClass = window.AudioContext || window.webkitAudioContext;
|
|
138
|
-
if (!AudioContextClass) {
|
|
139
|
-
throw new Error("\u60A8\u7684\u6D4F\u89C8\u5668\u4E0D\u652F\u6301Web Audio API");
|
|
140
|
-
}
|
|
141
|
-
const audioContext = new AudioContextClass();
|
|
142
|
-
if (audioContext.state === "suspended") {
|
|
143
|
-
await audioContext.resume();
|
|
144
|
-
}
|
|
145
|
-
return audioContext;
|
|
146
|
-
}
|
|
147
|
-
class StreamingFeatureExtractorService {
|
|
148
|
-
constructor() {
|
|
149
|
-
this.queue = new AsyncQueue();
|
|
150
|
-
/** 回调函数集合 */
|
|
151
|
-
this.callbacks = {};
|
|
152
|
-
/** 存储每个chunk的处理结果,用于最终合并 */
|
|
153
|
-
this.chunkResults = [];
|
|
154
|
-
/** 音频总chunk数 */
|
|
155
|
-
this.totalChunks = 0;
|
|
156
|
-
/** 已完成的chunk数 */
|
|
157
|
-
this.completedChunks = 0;
|
|
158
|
-
/** 已处理的总帧数累加器 */
|
|
159
|
-
this.totalFramesProcessed = 0;
|
|
160
|
-
/** 当前是否有任务在运行 */
|
|
161
|
-
this.isRunning = false;
|
|
162
|
-
/** 当前处理的音频缓冲区 */
|
|
163
|
-
this.audioBuffer = null;
|
|
164
|
-
/** 音频上下文实例,用于创建AudioBuffer */
|
|
165
|
-
this.audioContext = null;
|
|
166
|
-
/** 复用的Worker实例,以提高性能 */
|
|
167
|
-
this.worker = null;
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* 创建AudioBuffer的兼容方法,处理Safari的限制。
|
|
171
|
-
* @param numberOfChannels 声道数
|
|
172
|
-
* @param length 缓冲区长度
|
|
173
|
-
* @param sampleRate 采样率
|
|
174
|
-
* @returns AudioBuffer实例
|
|
175
|
-
*/
|
|
176
|
-
async createAudioBuffer(numberOfChannels, length, sampleRate) {
|
|
177
|
-
try {
|
|
178
|
-
return new AudioBuffer({
|
|
179
|
-
numberOfChannels,
|
|
180
|
-
length,
|
|
181
|
-
sampleRate
|
|
182
|
-
});
|
|
183
|
-
} catch {
|
|
184
|
-
if (!this.audioContext) {
|
|
185
|
-
const AudioContextClass = window.AudioContext || window.webkitAudioContext;
|
|
186
|
-
this.audioContext = new AudioContextClass();
|
|
187
|
-
}
|
|
188
|
-
return this.audioContext.createBuffer(
|
|
189
|
-
numberOfChannels,
|
|
190
|
-
length,
|
|
191
|
-
sampleRate
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* 使用Worker处理AudioBuffer,提取特征
|
|
197
|
-
* @param audioBuffer 输入的AudioBuffer
|
|
198
|
-
* @returns Promise<FeatureExtractionResult>
|
|
199
|
-
*/
|
|
200
|
-
async processWithWorker(audioBuffer) {
|
|
201
|
-
if (!this.worker) {
|
|
202
|
-
throw new Error("Worker not initialized");
|
|
203
|
-
}
|
|
204
|
-
return new Promise((resolve, reject) => {
|
|
205
|
-
const cleanup = () => {
|
|
206
|
-
if (this.worker) {
|
|
207
|
-
this.worker.removeEventListener("message", handleMessage);
|
|
208
|
-
this.worker.removeEventListener("error", handleError);
|
|
209
|
-
}
|
|
210
|
-
};
|
|
211
|
-
function handleMessage(event) {
|
|
212
|
-
cleanup();
|
|
213
|
-
if (event.data.status === "success") {
|
|
214
|
-
resolve(event.data.payload);
|
|
215
|
-
} else {
|
|
216
|
-
reject(new Error(event.data.error || "Unknown worker error"));
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
function handleError(error) {
|
|
220
|
-
cleanup();
|
|
221
|
-
reject(error);
|
|
222
|
-
}
|
|
223
|
-
this.worker.addEventListener("message", handleMessage);
|
|
224
|
-
this.worker.addEventListener("error", handleError);
|
|
225
|
-
const leftChannel = audioBuffer.getChannelData(0);
|
|
226
|
-
const rightChannel = audioBuffer.numberOfChannels > 1 ? audioBuffer.getChannelData(1) : void 0;
|
|
227
|
-
const message = {
|
|
228
|
-
leftChannel,
|
|
229
|
-
rightChannel,
|
|
230
|
-
sampleRate: audioBuffer.sampleRate
|
|
231
|
-
};
|
|
232
|
-
const transferList = [leftChannel.buffer, rightChannel?.buffer].filter(Boolean);
|
|
233
|
-
this.worker.postMessage(message, transferList);
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
/**
|
|
237
|
-
* 启动流式音频特征提取。
|
|
238
|
-
* @param audioBuffer 输入的音频缓冲区。
|
|
239
|
-
* @param callbacks 处理过程中的回调函数集合。
|
|
240
|
-
*/
|
|
241
|
-
async processStreaming(audioBuffer, callbacks) {
|
|
242
|
-
await this.queue.wait();
|
|
243
|
-
if (this.isRunning) {
|
|
244
|
-
throw new Error("\u53E6\u4E00\u4E2A\u97F3\u9891\u6B63\u5728\u5904\u7406\u4E2D\uFF0C\u8BF7\u7A0D\u5019\u3002");
|
|
245
|
-
}
|
|
246
|
-
this.isRunning = true;
|
|
247
|
-
this.audioBuffer = audioBuffer;
|
|
248
|
-
this.callbacks = callbacks;
|
|
249
|
-
this.chunkResults = [];
|
|
250
|
-
this.completedChunks = 0;
|
|
251
|
-
this.totalFramesProcessed = 0;
|
|
252
|
-
if (!this.worker) {
|
|
253
|
-
this.worker = new Worker(
|
|
254
|
-
/* @vite-ignore */
|
|
255
|
-
`${workerConfig.path}/feature.worker.js`,
|
|
256
|
-
{ type: "module" }
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
const durationSeconds = audioBuffer.duration;
|
|
260
|
-
this.totalChunks = Math.ceil(durationSeconds / CHUNK_DURATION_SECONDS);
|
|
261
|
-
try {
|
|
262
|
-
for (let chunkIndex = 0; chunkIndex < this.totalChunks; chunkIndex++) {
|
|
263
|
-
if (!this.isRunning)
|
|
264
|
-
break;
|
|
265
|
-
await this.processChunk(chunkIndex);
|
|
266
|
-
}
|
|
267
|
-
if (this.isRunning) {
|
|
268
|
-
await this.finalize();
|
|
269
|
-
}
|
|
270
|
-
} catch (error) {
|
|
271
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
272
|
-
this.callbacks.onError?.(errorMessage);
|
|
273
|
-
} finally {
|
|
274
|
-
await this.queue.shift();
|
|
275
|
-
this.cleanup();
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* 处理单个音频chunk,提取特征并回调。
|
|
280
|
-
* @param chunkIndex chunk的索引(从0开始)。
|
|
281
|
-
*/
|
|
282
|
-
async processChunk(chunkIndex) {
|
|
283
|
-
if (!this.audioBuffer || !this.worker)
|
|
284
|
-
return;
|
|
285
|
-
const startTimeSeconds = chunkIndex * CHUNK_DURATION_SECONDS;
|
|
286
|
-
const endTimeSeconds = Math.min(
|
|
287
|
-
(chunkIndex + 1) * CHUNK_DURATION_SECONDS,
|
|
288
|
-
this.audioBuffer.duration
|
|
289
|
-
);
|
|
290
|
-
const startSample = Math.floor(
|
|
291
|
-
startTimeSeconds * this.audioBuffer.sampleRate
|
|
292
|
-
);
|
|
293
|
-
const endSample = Math.floor(endTimeSeconds * this.audioBuffer.sampleRate);
|
|
294
|
-
const chunkLength = endSample - startSample;
|
|
295
|
-
const chunkBuffer = await this.createAudioBuffer(
|
|
296
|
-
this.audioBuffer.numberOfChannels,
|
|
297
|
-
chunkLength,
|
|
298
|
-
this.audioBuffer.sampleRate
|
|
299
|
-
);
|
|
300
|
-
for (let channel = 0; channel < this.audioBuffer.numberOfChannels; channel++) {
|
|
301
|
-
const sourceData = this.audioBuffer.getChannelData(channel);
|
|
302
|
-
const chunkData = chunkBuffer.getChannelData(channel);
|
|
303
|
-
chunkData.set(sourceData.subarray(startSample, endSample));
|
|
304
|
-
}
|
|
305
|
-
const result = await this.processWithWorker(chunkBuffer);
|
|
306
|
-
const chunkResult = {
|
|
307
|
-
chunkIndex,
|
|
308
|
-
features: result.features,
|
|
309
|
-
dimensions: result.dimensions,
|
|
310
|
-
startTimeSeconds,
|
|
311
|
-
endTimeSeconds
|
|
312
|
-
};
|
|
313
|
-
this.totalFramesProcessed += chunkResult.dimensions[0];
|
|
314
|
-
this.chunkResults[chunkIndex] = chunkResult;
|
|
315
|
-
this.completedChunks++;
|
|
316
|
-
this.callbacks.onChunkComplete?.(chunkResult);
|
|
317
|
-
this.callbacks.onProgress?.(this.completedChunks, this.totalChunks);
|
|
318
|
-
this.chunkResults[chunkIndex] = null;
|
|
319
|
-
}
|
|
320
|
-
/**
|
|
321
|
-
* 所有chunk处理完成后,合并特征并调用onComplete。
|
|
322
|
-
*/
|
|
323
|
-
async finalize() {
|
|
324
|
-
if (this.completedChunks === 0) {
|
|
325
|
-
this.callbacks.onError?.("No chunks were processed.");
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
const totalDimensions = [
|
|
329
|
-
this.totalFramesProcessed,
|
|
330
|
-
NUM_SEQUENCE_FRAMES,
|
|
331
|
-
NUM_MEL_BINS
|
|
332
|
-
];
|
|
333
|
-
this.callbacks.onComplete?.(totalDimensions);
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* 清理内部状态和资源。
|
|
337
|
-
*/
|
|
338
|
-
cleanup() {
|
|
339
|
-
this.isRunning = false;
|
|
340
|
-
this.audioBuffer = null;
|
|
341
|
-
if (this.worker) {
|
|
342
|
-
this.worker.terminate();
|
|
343
|
-
this.worker = null;
|
|
344
|
-
}
|
|
345
|
-
this.chunkResults.forEach((chunk) => {
|
|
346
|
-
if (chunk && chunk.features) {
|
|
347
|
-
chunk.features = null;
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
this.chunkResults = [];
|
|
351
|
-
this.callbacks = {};
|
|
352
|
-
this.completedChunks = 0;
|
|
353
|
-
this.totalChunks = 0;
|
|
354
|
-
this.totalFramesProcessed = 0;
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* 停止当前的流式处理。
|
|
358
|
-
*/
|
|
359
|
-
stop() {
|
|
360
|
-
if (this.isRunning) {
|
|
361
|
-
this.isRunning = false;
|
|
362
|
-
if (this.worker) {
|
|
363
|
-
this.worker.terminate();
|
|
364
|
-
this.worker = null;
|
|
365
|
-
}
|
|
366
|
-
this.callbacks = {};
|
|
367
|
-
this.chunkResults = [];
|
|
368
|
-
this.totalChunks = 0;
|
|
369
|
-
this.completedChunks = 0;
|
|
370
|
-
this.totalFramesProcessed = 0;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
export { StreamingFeatureExtractorService, createAudioContext };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file 共享常量
|
|
3
|
-
*
|
|
4
|
-
* 存储在多个服务中使用的常量,避免重复定义
|
|
5
|
-
*
|
|
6
|
-
* @author Zhonghan Li
|
|
7
|
-
*/
|
|
8
|
-
/** Mel 滤波器组的数量 */
|
|
9
|
-
export declare const NUM_MEL_BINS = 80;
|
|
10
|
-
/** 用于后续处理的序列帧数(每个块包含的帧数) */
|
|
11
|
-
export declare const NUM_SEQUENCE_FRAMES = 4;
|
|
12
|
-
/** 每个chunk的时长(秒) */
|
|
13
|
-
export declare const CHUNK_DURATION_SECONDS = 5;
|
|
14
|
-
/** 上下文窗口中的总块数 */
|
|
15
|
-
export declare const WINDOW_NUM_CHUNKS = 32;
|
|
16
|
-
/** 上下文窗口的单边块数(半径) */
|
|
17
|
-
export declare const WINDOW_NUM_CHUNKS_HALF: number;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
export { C as CHUNK_DURATION_SECONDS, N as NUM_MEL_BINS, a as NUM_SEQUENCE_FRAMES, W as WINDOW_NUM_CHUNKS, b as WINDOW_NUM_CHUNKS_HALF } from '../../../constants.mjs';
|
|
2
|
-
|
|
3
|
-
function normalizeError(error) {
|
|
4
|
-
if (error instanceof Error) {
|
|
5
|
-
return error.message;
|
|
6
|
-
}
|
|
7
|
-
if (typeof error === "string") {
|
|
8
|
-
return error;
|
|
9
|
-
}
|
|
10
|
-
return "Unknown error occurred";
|
|
11
|
-
}
|
|
12
|
-
function createErrorHandler(context, _fallbackMessage = "Operation failed") {
|
|
13
|
-
return function(error) {
|
|
14
|
-
const errorMessage = normalizeError(error);
|
|
15
|
-
const fullMessage = `${context}: ${errorMessage}`;
|
|
16
|
-
console.error(fullMessage);
|
|
17
|
-
throw new Error(fullMessage);
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
function isNonNull(value) {
|
|
21
|
-
return value !== null && value !== void 0;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export { createErrorHandler, isNonNull, normalizeError };
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file 共享工具函数
|
|
3
|
-
*
|
|
4
|
-
* 提供基础错误处理工具函数
|
|
5
|
-
*
|
|
6
|
-
* @author Zhonghan Li
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* 标准化错误对象
|
|
10
|
-
* @param error 原始错误
|
|
11
|
-
* @returns 标准化的错误消息
|
|
12
|
-
*/
|
|
13
|
-
export declare function normalizeError(error: unknown): string;
|
|
14
|
-
/**
|
|
15
|
-
* 创建错误处理包装器
|
|
16
|
-
* @param context 错误上下文
|
|
17
|
-
* @param fallbackMessage 备用错误消息
|
|
18
|
-
* @returns 错误处理函数
|
|
19
|
-
*/
|
|
20
|
-
export declare function createErrorHandler(context: string, _fallbackMessage?: string): (error: unknown) => never;
|
|
21
|
-
/**
|
|
22
|
-
* 检查值是否为非空
|
|
23
|
-
* @param value 要检查的值
|
|
24
|
-
* @returns 类型守卫结果
|
|
25
|
-
*/
|
|
26
|
-
export declare function isNonNull<T>(value: T | null | undefined): value is T;
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file StreamingInferenceService.ts
|
|
3
|
-
*
|
|
4
|
-
* 流式推理服务模块,支持增量推理模式。
|
|
5
|
-
* 1. 每收到一个chunk的音频特征后,立即开始推理该部分的视频帧,显著降低首帧延迟。
|
|
6
|
-
* 2. 支持推理进度、帧级回调、chunk完成回调、全部完成回调及错误处理。
|
|
7
|
-
*
|
|
8
|
-
* @author Zhonghan Li
|
|
9
|
-
*/
|
|
10
|
-
import type { ChunkFeatureResult } from '../audio/StreamingFeatureExtractorService';
|
|
11
|
-
import type { ImageDataResponse } from '../media/DataLoaderService';
|
|
12
|
-
/**
|
|
13
|
-
* Worker initialization message
|
|
14
|
-
*/
|
|
15
|
-
export interface StreamingWorkerInitMessage {
|
|
16
|
-
type: 'init';
|
|
17
|
-
modelPath: string;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Main thread message types for worker communication
|
|
21
|
-
*/
|
|
22
|
-
export interface MainThreadFrameMessage {
|
|
23
|
-
type: 'frame';
|
|
24
|
-
payload: {
|
|
25
|
-
frame: ImageBitmap;
|
|
26
|
-
frameIndex: number;
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
export interface MainThreadChunkCompleteMessage {
|
|
30
|
-
type: 'chunk_complete';
|
|
31
|
-
payload: {
|
|
32
|
-
chunkIndex: number;
|
|
33
|
-
timings: Record<string, number>;
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
export interface MainThreadProgressMessage {
|
|
37
|
-
type: 'progress';
|
|
38
|
-
payload: {
|
|
39
|
-
processed: number;
|
|
40
|
-
total: number;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export interface MainThreadAllCompleteMessage {
|
|
44
|
-
type: 'all_complete';
|
|
45
|
-
payload: Record<string, number>;
|
|
46
|
-
}
|
|
47
|
-
export interface MainThreadErrorMessage {
|
|
48
|
-
type: 'error';
|
|
49
|
-
payload: string;
|
|
50
|
-
}
|
|
51
|
-
export interface MainThreadReadyMessage {
|
|
52
|
-
type: 'ready';
|
|
53
|
-
}
|
|
54
|
-
export type MainThreadMessage = MainThreadFrameMessage | MainThreadChunkCompleteMessage | MainThreadProgressMessage | MainThreadAllCompleteMessage | MainThreadErrorMessage | MainThreadReadyMessage;
|
|
55
|
-
/**
|
|
56
|
-
* 单个流式推理chunk的数据结构。
|
|
57
|
-
*/
|
|
58
|
-
export interface StreamingChunkData {
|
|
59
|
-
/** chunk索引(从0开始) */
|
|
60
|
-
chunkIndex: number;
|
|
61
|
-
/** 音频特征(Float32Array) */
|
|
62
|
-
audioFeatures: Float32Array;
|
|
63
|
-
/** 特征维度信息 [帧数, 序列长度, mel频带数] */
|
|
64
|
-
audioDimensions: number[];
|
|
65
|
-
/** 该chunk对应的起始帧索引 */
|
|
66
|
-
startFrame: number;
|
|
67
|
-
/** 该chunk对应的结束帧索引 */
|
|
68
|
-
endFrame: number;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* 发送给worker的流式推理消息类型。
|
|
72
|
-
*/
|
|
73
|
-
export interface StreamingWorkerRunMessage {
|
|
74
|
-
/** 消息类型,固定为 "streaming_run" */
|
|
75
|
-
type: 'streaming_run';
|
|
76
|
-
/** 当前chunk的数据 */
|
|
77
|
-
chunkData: StreamingChunkData;
|
|
78
|
-
/** 图像数据集(已在init_streaming中发送,此处占位) */
|
|
79
|
-
dataset: ImageDataResponse;
|
|
80
|
-
/** 图像Blob映射(已在init_streaming中发送,此处占位) */
|
|
81
|
-
zipBlob: Blob;
|
|
82
|
-
/** 融合掩码(已在init_streaming中发送,此处占位) */
|
|
83
|
-
blendingMaskBitmap: ImageBitmap;
|
|
84
|
-
/** 起始图像帧索引 */
|
|
85
|
-
startImageIndex: number;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* 流式推理回调接口定义。
|
|
89
|
-
*/
|
|
90
|
-
export interface StreamingInferenceCallbacks {
|
|
91
|
-
/**
|
|
92
|
-
* 推理进度回调。
|
|
93
|
-
* @param processed 已处理帧数
|
|
94
|
-
* @param total 总帧数
|
|
95
|
-
*/
|
|
96
|
-
onProgress?: (processed: number, total: number) => void;
|
|
97
|
-
/**
|
|
98
|
-
* 单帧推理完成回调。
|
|
99
|
-
* @param frame 生成的视频帧
|
|
100
|
-
* @param frameIndex 帧索引
|
|
101
|
-
*/
|
|
102
|
-
onFrame?: (frame: ImageBitmap, frameIndex: number) => void;
|
|
103
|
-
/**
|
|
104
|
-
* 单个chunk推理完成回调。
|
|
105
|
-
* @param chunkIndex chunk索引
|
|
106
|
-
* @param timings 该chunk的耗时统计
|
|
107
|
-
*/
|
|
108
|
-
onChunkComplete?: (chunkIndex: number, timings: Record<string, number>) => void;
|
|
109
|
-
/**
|
|
110
|
-
* 所有chunk推理完成回调。
|
|
111
|
-
* @param totalTimings 总耗时统计
|
|
112
|
-
*/
|
|
113
|
-
onAllComplete?: (totalTimings: Record<string, number>) => void;
|
|
114
|
-
/**
|
|
115
|
-
* 推理错误回调。
|
|
116
|
-
* @param message 错误信息
|
|
117
|
-
*/
|
|
118
|
-
onError?: (message: string) => void;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* 流式推理服务主类。
|
|
122
|
-
*/
|
|
123
|
-
export declare class StreamingInferenceService {
|
|
124
|
-
/** 后台推理Worker实例 */
|
|
125
|
-
private worker;
|
|
126
|
-
/** 推理回调函数集合 */
|
|
127
|
-
private callbacks;
|
|
128
|
-
/** Worker是否已初始化 */
|
|
129
|
-
private isInitialized;
|
|
130
|
-
/** 待推理的chunk数据映射 */
|
|
131
|
-
private pendingChunks;
|
|
132
|
-
/** 已完成推理的chunk索引集合 */
|
|
133
|
-
private completedChunks;
|
|
134
|
-
/** 当前累计的总帧数 */
|
|
135
|
-
private totalFrames;
|
|
136
|
-
/** 是否处于推理处理中 */
|
|
137
|
-
private isProcessing;
|
|
138
|
-
/** 全局chunk索引计数器,确保每个chunk有唯一索引 */
|
|
139
|
-
private globalChunkIndex;
|
|
140
|
-
private _def;
|
|
141
|
-
/**
|
|
142
|
-
* 构造函数,初始化Worker并注册消息回调。
|
|
143
|
-
* @param modelPath 模型路径
|
|
144
|
-
* @param onReady Worker初始化完成回调
|
|
145
|
-
*/
|
|
146
|
-
constructor(modelPath: string, onReady?: () => void);
|
|
147
|
-
/**
|
|
148
|
-
* 判断推理服务是否已准备就绪。
|
|
149
|
-
* @returns 是否已初始化
|
|
150
|
-
*/
|
|
151
|
-
isReady(): boolean;
|
|
152
|
-
when(): Promise<void>;
|
|
153
|
-
/**
|
|
154
|
-
* 启动流式推理流程,发送共享数据到worker。
|
|
155
|
-
* @param sharedData 所有chunk共享的数据(图像、配置等)
|
|
156
|
-
* @param callbacks 推理过程中的回调函数
|
|
157
|
-
* @param startImageIndex 起始图像帧索引,默认为0
|
|
158
|
-
*/
|
|
159
|
-
startStreaming(sharedData: {
|
|
160
|
-
dataset: ImageDataResponse;
|
|
161
|
-
zipBlob: Blob;
|
|
162
|
-
blendingMaskBitmap: ImageBitmap;
|
|
163
|
-
}, callbacks: StreamingInferenceCallbacks, startImageIndex?: number): Promise<void>;
|
|
164
|
-
/**
|
|
165
|
-
* 添加一个chunk进行推理。
|
|
166
|
-
* @param chunkResult 特征提取完成的chunk
|
|
167
|
-
*/
|
|
168
|
-
addChunk(chunkResult: ChunkFeatureResult): void;
|
|
169
|
-
/**
|
|
170
|
-
* 通知所有chunk都已添加完成,发送总帧数到worker。
|
|
171
|
-
*/
|
|
172
|
-
finishAddingChunks(): void;
|
|
173
|
-
/**
|
|
174
|
-
* 清理内部状态和资源。
|
|
175
|
-
*/
|
|
176
|
-
private cleanup;
|
|
177
|
-
/**
|
|
178
|
-
* 轻度清理,保持连续性,用于音频文件间的清理。
|
|
179
|
-
*/
|
|
180
|
-
private lightCleanup;
|
|
181
|
-
/**
|
|
182
|
-
* 停止推理并清理资源,通知worker停止。
|
|
183
|
-
*/
|
|
184
|
-
stop(): void;
|
|
185
|
-
/**
|
|
186
|
-
* 彻底终止worker并清理所有资源。
|
|
187
|
-
*/
|
|
188
|
-
terminate(): void;
|
|
189
|
-
}
|