@spatialwalk/avatarkit 1.0.0-beta.77 → 1.0.0-beta.78
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/CHANGELOG.md +14 -0
- package/dist/{StreamingAudioPlayer-BtOgYxcz.js → StreamingAudioPlayer-BNj8LpDw.js} +9 -79
- package/dist/avatar_core_wasm-e68766db.wasm +0 -0
- package/dist/core/AvatarController.d.ts +0 -1
- package/dist/core/AvatarManager.d.ts +12 -3
- package/dist/core/AvatarView.d.ts +33 -0
- package/dist/{index-CTh2onXJ.js → index-DEJMvfST.js} +581 -2764
- package/dist/index.js +1 -1
- package/dist/internal/constants.d.ts +102 -0
- package/dist/internal/index.d.ts +7 -0
- package/dist/renderer/webgpu/webgpuRenderer.d.ts +0 -57
- package/dist/utils/animation-interpolation.d.ts +3 -1
- package/dist/wasm/avatarCoreAdapter.d.ts +0 -49
- package/dist/wasm/avatarCoreMemory.d.ts +0 -13
- package/package.json +13 -14
- package/dist/renderer/webgpu/flameGPUBuffers.d.ts +0 -137
- package/dist/renderer/webgpu/flamePipeline.d.ts +0 -71
- package/dist/renderer/webgpu/gpuRadixSort.d.ts +0 -47
- package/dist/renderer/webgpu/transformPipeline.d.ts +0 -97
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.0-beta.78] - 2026-02-04
|
|
9
|
+
|
|
10
|
+
### 🐛 Bugfixes
|
|
11
|
+
- **Telemetry Accuracy** - Fixed environment field reporting issue in analytics events
|
|
12
|
+
|
|
13
|
+
### ✨ New Features
|
|
14
|
+
- **Performance Monitoring** - Added loading performance metrics for avatar resources
|
|
15
|
+
- `fetch_avatar_latency` - Total loading time
|
|
16
|
+
- `fetch_avatar_metadata_latency` - Metadata fetch time
|
|
17
|
+
- `download_avatar_assets_latency` - Asset download time
|
|
18
|
+
|
|
19
|
+
### 🔧 Improvements
|
|
20
|
+
- **Internal Architecture** - Unified configuration constants across platforms for better maintainability
|
|
21
|
+
|
|
8
22
|
## [1.0.0-beta.74] - 2026-01-22
|
|
9
23
|
|
|
10
24
|
### 🔧 Improvements
|
|
@@ -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-
|
|
4
|
+
import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-DEJMvfST.js";
|
|
5
5
|
class StreamingAudioPlayer {
|
|
6
6
|
// Mark if AudioContext is being resumed, avoid concurrent resume requests
|
|
7
7
|
constructor(options) {
|
|
@@ -40,18 +40,6 @@ 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)- 牺牲一点延迟换取流畅播放
|
|
55
43
|
// Volume control
|
|
56
44
|
__publicField(this, "gainNode", null);
|
|
57
45
|
__publicField(this, "volume", 1);
|
|
@@ -165,37 +153,6 @@ class StreamingAudioPlayer {
|
|
|
165
153
|
}
|
|
166
154
|
}
|
|
167
155
|
}
|
|
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
|
-
}
|
|
199
156
|
/**
|
|
200
157
|
* Add audio chunk (16-bit PCM)
|
|
201
158
|
*/
|
|
@@ -209,29 +166,14 @@ class StreamingAudioPlayer {
|
|
|
209
166
|
logger.errorWithError("[StreamingAudioPlayer] Failed to ensure AudioContext running in addChunk:", err);
|
|
210
167
|
});
|
|
211
168
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
-
}
|
|
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
|
+
});
|
|
235
177
|
if (this.autoContinue && this.isPaused) {
|
|
236
178
|
this.log("[StreamingAudioPlayer] autoContinue=true, auto-resuming playback");
|
|
237
179
|
this.autoContinue = false;
|
|
@@ -259,9 +201,6 @@ class StreamingAudioPlayer {
|
|
|
259
201
|
this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
260
202
|
this.audioChunks = [];
|
|
261
203
|
this.scheduledChunks = 0;
|
|
262
|
-
this.pendingChunks = [];
|
|
263
|
-
this.pendingBytesTotal = 0;
|
|
264
|
-
this.isFirstMerge = true;
|
|
265
204
|
this.pausedTimeOffset = 0;
|
|
266
205
|
this.pausedAt = 0;
|
|
267
206
|
this.pausedAudioContextTime = 0;
|
|
@@ -571,9 +510,6 @@ class StreamingAudioPlayer {
|
|
|
571
510
|
this.activeSources.clear();
|
|
572
511
|
this.audioChunks = [];
|
|
573
512
|
this.scheduledChunks = 0;
|
|
574
|
-
this.pendingChunks = [];
|
|
575
|
-
this.pendingBytesTotal = 0;
|
|
576
|
-
this.isFirstMerge = true;
|
|
577
513
|
this.autoContinue = false;
|
|
578
514
|
this.log("[StreamingAudioPlayer] Playback stopped, state reset");
|
|
579
515
|
}
|
|
@@ -634,9 +570,6 @@ class StreamingAudioPlayer {
|
|
|
634
570
|
}
|
|
635
571
|
this.audioChunks = [];
|
|
636
572
|
this.scheduledChunks = 0;
|
|
637
|
-
this.pendingChunks = [];
|
|
638
|
-
this.pendingBytesTotal = 0;
|
|
639
|
-
this.isFirstMerge = true;
|
|
640
573
|
this.sessionStartTime = 0;
|
|
641
574
|
this.pausedTimeOffset = 0;
|
|
642
575
|
this.pausedAt = 0;
|
|
@@ -656,9 +589,6 @@ class StreamingAudioPlayer {
|
|
|
656
589
|
this.stop();
|
|
657
590
|
this.audioChunks = [];
|
|
658
591
|
this.scheduledChunks = 0;
|
|
659
|
-
this.pendingChunks = [];
|
|
660
|
-
this.pendingBytesTotal = 0;
|
|
661
|
-
this.isFirstMerge = true;
|
|
662
592
|
this.sessionStartTime = 0;
|
|
663
593
|
this.pausedAt = 0;
|
|
664
594
|
this.scheduledTime = 0;
|
|
File without changes
|
|
@@ -4,9 +4,12 @@ export declare class AvatarManager {
|
|
|
4
4
|
private static _instance;
|
|
5
5
|
private avatarDownloader;
|
|
6
6
|
private avatarCache;
|
|
7
|
-
|
|
7
|
+
/** 下载队列:FIFO 顺序 */
|
|
8
8
|
private downloadQueue;
|
|
9
|
-
|
|
9
|
+
/** 当前正在执行的任务 */
|
|
10
|
+
private currentTask;
|
|
11
|
+
/** 任务索引:快速查找 id 对应的任务 */
|
|
12
|
+
private taskIndex;
|
|
10
13
|
/**
|
|
11
14
|
* Access via global singleton
|
|
12
15
|
*/
|
|
@@ -18,6 +21,12 @@ export declare class AvatarManager {
|
|
|
18
21
|
* @returns Promise<Avatar>
|
|
19
22
|
*/
|
|
20
23
|
load(id: string, onProgress?: (progress: LoadProgressInfo) => void): Promise<Avatar>;
|
|
24
|
+
/**
|
|
25
|
+
* Cancel a pending or running download task
|
|
26
|
+
* @param id Avatar ID to cancel
|
|
27
|
+
* @returns true if task was found and cancelled
|
|
28
|
+
*/
|
|
29
|
+
cancelLoad(id: string): boolean;
|
|
21
30
|
/**
|
|
22
31
|
* Get cached avatar
|
|
23
32
|
* @param id Avatar ID
|
|
@@ -30,7 +39,7 @@ export declare class AvatarManager {
|
|
|
30
39
|
*/
|
|
31
40
|
clear(id: string): void;
|
|
32
41
|
/**
|
|
33
|
-
* Clear all avatar cache and
|
|
42
|
+
* Clear all avatar cache and cancel all tasks
|
|
34
43
|
*/
|
|
35
44
|
clearAll(): void;
|
|
36
45
|
}
|
|
@@ -30,6 +30,7 @@ export declare class AvatarView {
|
|
|
30
30
|
private characterHandle;
|
|
31
31
|
private characterId;
|
|
32
32
|
private isPureRenderingMode;
|
|
33
|
+
private _renderingEnabled;
|
|
33
34
|
private avatarActiveTimer;
|
|
34
35
|
private readonly AVATAR_ACTIVE_INTERVAL;
|
|
35
36
|
/**
|
|
@@ -61,6 +62,38 @@ export declare class AvatarView {
|
|
|
61
62
|
frameCount: number;
|
|
62
63
|
useLinear?: boolean;
|
|
63
64
|
}): Promise<KeyframeData[]>;
|
|
65
|
+
/**
|
|
66
|
+
* Pause rendering loop
|
|
67
|
+
*
|
|
68
|
+
* When called:
|
|
69
|
+
* - Rendering loop stops (no GPU/canvas updates)
|
|
70
|
+
* - Audio playback continues normally
|
|
71
|
+
* - Animation state machine continues running
|
|
72
|
+
*
|
|
73
|
+
* Use `resumeRendering()` to resume rendering.
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* // Stop rendering to save GPU resources (audio continues)
|
|
77
|
+
* avatarView.pauseRendering()
|
|
78
|
+
*/
|
|
79
|
+
pauseRendering(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Resume rendering loop
|
|
82
|
+
*
|
|
83
|
+
* When called:
|
|
84
|
+
* - Rendering loop resumes from current state
|
|
85
|
+
* - If in Idle state, immediately renders current frame to restore display
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* // Resume rendering
|
|
89
|
+
* avatarView.resumeRendering()
|
|
90
|
+
*/
|
|
91
|
+
resumeRendering(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Check if rendering is currently enabled
|
|
94
|
+
* @returns true if rendering is enabled, false if paused
|
|
95
|
+
*/
|
|
96
|
+
isRenderingEnabled(): boolean;
|
|
64
97
|
/**
|
|
65
98
|
* Get or set avatar transform in canvas
|
|
66
99
|
*
|