@spatialwalk/avatarkit 1.0.0-beta.75 → 1.0.0-beta.77

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.
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-DktjmkwV.js";
4
+ import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-CTh2onXJ.js";
5
5
  class StreamingAudioPlayer {
6
6
  // Mark if AudioContext is being resumed, avoid concurrent resume requests
7
7
  constructor(options) {
@@ -40,6 +40,18 @@ class StreamingAudioPlayer {
40
40
  // Timestamp of last getCurrentTime log (for throttling)
41
41
  // Track start time (absolute time) and duration of each scheduled chunk for accurate current playback time calculation
42
42
  __publicField(this, "scheduledChunkInfo", []);
43
+ // 🚀 Chunk 合并优化:减少 AudioNode 创建数量
44
+ __publicField(this, "pendingChunks", []);
45
+ // 待合并的小 chunk
46
+ __publicField(this, "pendingBytesTotal", 0);
47
+ // 待合并的总字节数
48
+ __publicField(this, "isFirstMerge", true);
49
+ // 是否是首次合并(用于首包缓冲)
50
+ // 合并阈值:动态计算
51
+ __publicField(this, "MERGE_THRESHOLD_MS", 500);
52
+ // 后续合并阈值(500ms)
53
+ __publicField(this, "FIRST_BUFFER_THRESHOLD_MS", 250);
54
+ // 首包缓冲阈值(250ms)- 牺牲一点延迟换取流畅播放
43
55
  // Volume control
44
56
  __publicField(this, "gainNode", null);
45
57
  __publicField(this, "volume", 1);
@@ -153,6 +165,37 @@ class StreamingAudioPlayer {
153
165
  }
154
166
  }
155
167
  }
168
+ /**
169
+ * 🚀 合并待处理的小 chunks 成一个大 chunk
170
+ */
171
+ mergePendingChunks() {
172
+ if (this.pendingChunks.length === 0) {
173
+ return new Uint8Array(0);
174
+ }
175
+ if (this.pendingChunks.length === 1) {
176
+ const result = this.pendingChunks[0];
177
+ this.pendingChunks = [];
178
+ this.pendingBytesTotal = 0;
179
+ return result;
180
+ }
181
+ const merged = new Uint8Array(this.pendingBytesTotal);
182
+ let offset = 0;
183
+ for (const chunk of this.pendingChunks) {
184
+ merged.set(chunk, offset);
185
+ offset += chunk.length;
186
+ }
187
+ this.pendingChunks = [];
188
+ this.pendingBytesTotal = 0;
189
+ return merged;
190
+ }
191
+ /**
192
+ * 计算合并阈值(基于实际采样率)
193
+ * @param isFirst 是否是首次合并(使用较小的首包缓冲阈值)
194
+ */
195
+ getMergeThresholdBytes(isFirst = false) {
196
+ const thresholdMs = isFirst ? this.FIRST_BUFFER_THRESHOLD_MS : this.MERGE_THRESHOLD_MS;
197
+ return this.sampleRate * this.channelCount * 2 * (thresholdMs / 1e3);
198
+ }
156
199
  /**
157
200
  * Add audio chunk (16-bit PCM)
158
201
  */
@@ -166,14 +209,29 @@ class StreamingAudioPlayer {
166
209
  logger.errorWithError("[StreamingAudioPlayer] Failed to ensure AudioContext running in addChunk:", err);
167
210
  });
168
211
  }
169
- this.audioChunks.push({ data: pcmData, isLast });
170
- this.log(`Added chunk ${this.audioChunks.length}`, {
171
- size: pcmData.length,
172
- totalChunks: this.audioChunks.length,
173
- isLast,
174
- isPlaying: this.isPlaying,
175
- scheduledChunks: this.scheduledChunks
176
- });
212
+ if (pcmData.length > 0) {
213
+ this.pendingChunks.push(pcmData);
214
+ this.pendingBytesTotal += pcmData.length;
215
+ }
216
+ const mergeThreshold = this.getMergeThresholdBytes(this.isFirstMerge);
217
+ const shouldMerge = isLast || this.pendingBytesTotal >= mergeThreshold;
218
+ if (shouldMerge && this.pendingBytesTotal > 0) {
219
+ const mergedData = this.mergePendingChunks();
220
+ this.audioChunks.push({ data: mergedData, isLast });
221
+ this.isFirstMerge = false;
222
+ this.log(`Added merged chunk ${this.audioChunks.length}`, {
223
+ size: mergedData.length,
224
+ totalChunks: this.audioChunks.length,
225
+ isLast,
226
+ isPlaying: this.isPlaying,
227
+ scheduledChunks: this.scheduledChunks
228
+ });
229
+ } else if (isLast && this.pendingBytesTotal === 0) {
230
+ this.audioChunks.push({ data: new Uint8Array(0), isLast: true });
231
+ } else {
232
+ this.log(`Accumulating chunk, pending: ${this.pendingBytesTotal} bytes (threshold: ${mergeThreshold}, isFirstMerge: ${this.isFirstMerge})`);
233
+ return;
234
+ }
177
235
  if (this.autoContinue && this.isPaused) {
178
236
  this.log("[StreamingAudioPlayer] autoContinue=true, auto-resuming playback");
179
237
  this.autoContinue = false;
@@ -201,6 +259,9 @@ class StreamingAudioPlayer {
201
259
  this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
202
260
  this.audioChunks = [];
203
261
  this.scheduledChunks = 0;
262
+ this.pendingChunks = [];
263
+ this.pendingBytesTotal = 0;
264
+ this.isFirstMerge = true;
204
265
  this.pausedTimeOffset = 0;
205
266
  this.pausedAt = 0;
206
267
  this.pausedAudioContextTime = 0;
@@ -510,6 +571,9 @@ class StreamingAudioPlayer {
510
571
  this.activeSources.clear();
511
572
  this.audioChunks = [];
512
573
  this.scheduledChunks = 0;
574
+ this.pendingChunks = [];
575
+ this.pendingBytesTotal = 0;
576
+ this.isFirstMerge = true;
513
577
  this.autoContinue = false;
514
578
  this.log("[StreamingAudioPlayer] Playback stopped, state reset");
515
579
  }
@@ -570,6 +634,9 @@ class StreamingAudioPlayer {
570
634
  }
571
635
  this.audioChunks = [];
572
636
  this.scheduledChunks = 0;
637
+ this.pendingChunks = [];
638
+ this.pendingBytesTotal = 0;
639
+ this.isFirstMerge = true;
573
640
  this.sessionStartTime = 0;
574
641
  this.pausedTimeOffset = 0;
575
642
  this.pausedAt = 0;
@@ -589,6 +656,9 @@ class StreamingAudioPlayer {
589
656
  this.stop();
590
657
  this.audioChunks = [];
591
658
  this.scheduledChunks = 0;
659
+ this.pendingChunks = [];
660
+ this.pendingBytesTotal = 0;
661
+ this.isFirstMerge = true;
592
662
  this.sessionStartTime = 0;
593
663
  this.pausedAt = 0;
594
664
  this.scheduledTime = 0;
File without changes
@@ -25,6 +25,7 @@ export declare class AvatarController {
25
25
  private renderCallback?;
26
26
  private characterHandle;
27
27
  private characterId;
28
+ private useGPUPath;
28
29
  private postProcessingConfig;
29
30
  private playbackLoopId;
30
31
  private lastRenderedFrameIndex;