@spatialwalk/avatarkit 1.0.0-beta.81 → 1.0.0-beta.83
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 -15
- package/README.md +54 -71
- package/dist/{StreamingAudioPlayer-B7UiWDWa.js → StreamingAudioPlayer-yvKtEWGz.js} +9 -79
- package/dist/core/AvatarController.d.ts +3 -28
- package/dist/core/AvatarView.d.ts +3 -5
- package/dist/{index-gUFUNqtF.js → index-CQqZD1OY.js} +12957 -15407
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/marble-test/src/billboard.d.ts +22 -0
- package/dist/marble-test/src/main.d.ts +6 -0
- package/dist/marble-test/src/spzLoader.d.ts +18 -0
- package/dist/next.d.ts +1 -12
- package/dist/tools/capture-messages/capture.d.ts +15 -0
- package/dist/tools/capture-messages/verify.d.ts +5 -0
- package/dist/tools/replay-web/vite.config.d.ts +2 -0
- package/dist/types/character.d.ts +37 -1
- package/dist/types/index.d.ts +9 -0
- package/next.d.ts +1 -12
- package/next.js +115 -25
- package/package.json +1 -1
- package/dist/animation/AnimationWebSocketClient.d.ts +0 -6
- package/dist/animation/utils/eventEmitter.d.ts +0 -5
- package/dist/animation/utils/flameConverter.d.ts +0 -5
- package/dist/audio/AnimationPlayer.d.ts +0 -6
- package/dist/audio/StreamingAudioPlayer.d.ts +0 -7
- package/dist/config/app-config.d.ts +0 -43
- package/dist/config/constants.d.ts +0 -21
- package/dist/config/sdk-config-loader.d.ts +0 -6
- package/dist/core/AvatarDownloader.d.ts +0 -15
- package/dist/core/NetworkLayer.d.ts +0 -7
- package/dist/generated/common/v1/models.d.ts +0 -36
- package/dist/generated/driveningress/v1/driveningress.d.ts +0 -79
- package/dist/generated/driveningress/v2/driveningress.d.ts +0 -162
- package/dist/generated/google/protobuf/struct.d.ts +0 -107
- package/dist/generated/google/protobuf/timestamp.d.ts +0 -128
- package/dist/internal/constants.d.ts +0 -102
- package/dist/internal/index.d.ts +0 -7
- package/dist/renderer/RenderSystem.d.ts +0 -9
- package/dist/renderer/covariance.d.ts +0 -5
- package/dist/renderer/renderer.d.ts +0 -12
- package/dist/renderer/sortSplats.d.ts +0 -5
- package/dist/renderer/webgl/reorderData.d.ts +0 -7
- package/dist/renderer/webgl/webglRenderer.d.ts +0 -96
- 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 -69
- package/dist/renderer/webgpu/transformPipeline.d.ts +0 -97
- package/dist/renderer/webgpu/webgpuRenderer.d.ts +0 -136
- package/dist/utils/animation-interpolation.d.ts +0 -8
- package/dist/utils/client-id.d.ts +0 -6
- package/dist/utils/conversationId.d.ts +0 -10
- package/dist/utils/error-utils.d.ts +0 -26
- package/dist/utils/id-manager.d.ts +0 -34
- package/dist/utils/log-sanitizer.d.ts +0 -15
- package/dist/utils/log-sink.d.ts +0 -47
- package/dist/utils/logger.d.ts +0 -25
- package/dist/utils/posthog-tracker.d.ts +0 -8
- package/dist/utils/pwa-cache-manager.d.ts +0 -17
- package/dist/utils/usage-tracker.d.ts +0 -6
- package/dist/wasm/avatarCoreAdapter.d.ts +0 -126
- package/dist/wasm/avatarCoreMemory.d.ts +0 -157
- /package/dist/{vanilla → marble-test}/vite.config.d.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,23 +5,22 @@ 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.
|
|
9
|
-
|
|
10
|
-
### 🔧 Performance Improvements
|
|
11
|
-
- **GPU Compute Mobile Compatibility** - Reduced all compute shader workgroup sizes from 256 to 128 for broader mobile GPU support
|
|
12
|
-
- FLAME pipeline shaders: ShapeBlend, PoseDeform, JointRegress, LBS, FaceGeometry
|
|
13
|
-
- Transform compute shader and Radix Sort shaders
|
|
14
|
-
- Fixes avatar not displaying on mobile devices with `maxComputeWorkgroupSizeX` = 128
|
|
15
|
-
- **GPU Radix Sort Single Submission** - Rewrote GPU Radix Sort to use a single `CommandEncoder` submission per frame
|
|
16
|
-
- Pre-allocated per-pass params buffers (static, no per-frame `writeBuffer` calls)
|
|
17
|
-
- Pre-created bind groups at initialization time
|
|
18
|
-
- Reduced GPU submit overhead from 12+ `device.queue.submit()` to 1 per frame
|
|
8
|
+
## [1.0.0-beta.83] - 2026-03-03
|
|
19
9
|
|
|
20
10
|
### 🐛 Bugfixes
|
|
21
|
-
- **
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
11
|
+
- **Playback Startup Sync** - Improved startup sequencing for SDK mode playback to reduce transition/audio desync at conversation start
|
|
12
|
+
- **Initialization Guard** - Added explicit `appId` validation in `AvatarSDK.initialize()` to fail fast when `appId` is empty
|
|
13
|
+
|
|
14
|
+
### 🔧 Improvements
|
|
15
|
+
- **Server Error Propagation** - `onError` now consistently receives `AvatarError` with server message and mapped error code for `MESSAGE_SERVER_ERROR` in SDK mode
|
|
16
|
+
|
|
17
|
+
### 📚 Documentation
|
|
18
|
+
- **README Error Callback** - Added detailed `onError` callback behavior and server error code mapping documentation
|
|
19
|
+
|
|
20
|
+
## [1.0.0-beta.82] - 2026-02-12
|
|
21
|
+
|
|
22
|
+
### 🔧 Improvements
|
|
23
|
+
- **Proto V2 协议更新** - 更新解码协议
|
|
25
24
|
|
|
26
25
|
## [1.0.0-beta.80] - 2026-02-08
|
|
27
26
|
|
package/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# AvatarKit SDK
|
|
2
2
|
|
|
3
|
-
Real-time virtual avatar rendering SDK
|
|
3
|
+
Real-time virtual avatar rendering SDK for Web, supporting audio-driven animation and high-quality 3D rendering.
|
|
4
4
|
|
|
5
5
|
## 🚀 Features
|
|
6
6
|
|
|
7
|
-
- **3D
|
|
8
|
-
- **Audio-Driven Real-Time Animation
|
|
7
|
+
- **High-Quality 3D Rendering** - GPU-accelerated avatar rendering with automatic backend selection
|
|
8
|
+
- **Audio-Driven Real-Time Animation** - Send audio data, SDK handles animation and rendering
|
|
9
9
|
- **Multi-Avatar Support** - Support multiple avatar instances simultaneously, each with independent state and rendering
|
|
10
|
-
- **WebGPU/WebGL Dual Rendering Backend** - Automatically selects the best rendering backend for compatibility
|
|
11
|
-
- **WASM High-Performance Computing** - Uses C++ compiled WebAssembly modules for geometric calculations
|
|
12
10
|
- **TypeScript Support** - Complete type definitions and IntelliSense
|
|
13
11
|
- **Modular Architecture** - Clear component separation, easy to integrate and extend
|
|
14
12
|
|
|
@@ -65,10 +63,6 @@ The plugin automatically handles:
|
|
|
65
63
|
|
|
66
64
|
- ✅ **Development Server**: Automatically sets the correct MIME type (`application/wasm`) for WASM files
|
|
67
65
|
- ✅ **Build Time**: Automatically copies WASM files to `dist/assets/` directory
|
|
68
|
-
- Smart Detection: Extracts referenced WASM file names (including hash) from JS glue files
|
|
69
|
-
- Auto Matching: Ensures copied WASM files match references in JS glue files
|
|
70
|
-
- Hash Support: Correctly handles hashed WASM files (e.g., `avatar_core_wasm-{hash}.wasm`)
|
|
71
|
-
- ✅ **WASM JS Glue**: Automatically copies WASM JS glue files to `dist/assets/` directory
|
|
72
66
|
- ✅ **Cloudflare Pages**: Automatically generates `_headers` file to ensure WASM files use the correct MIME type
|
|
73
67
|
- ✅ **Vite Configuration**: Automatically configures `optimizeDeps`, `assetsInclude`, `assetsInlineLimit`, and other options
|
|
74
68
|
|
|
@@ -128,7 +122,7 @@ export default withAvatarkit({
|
|
|
128
122
|
|
|
129
123
|
The plugin automatically handles:
|
|
130
124
|
|
|
131
|
-
- ✅ **
|
|
125
|
+
- ✅ **Path Fix**: Patches asset path resolution so WASM files are correctly loaded at `/_next/static/chunks/`
|
|
132
126
|
- ✅ **WASM Copying**: Copies `.wasm` files into `static/chunks/` via a custom webpack plugin (client build only)
|
|
133
127
|
- ✅ **Content-Type Headers**: Adds `application/wasm` response header for `/_next/static/chunks/*.wasm`
|
|
134
128
|
- ✅ **Config Chaining**: Preserves your existing `webpack` and `headers` configurations
|
|
@@ -146,7 +140,7 @@ The App ID is used to identify your application. You can obtain your App ID by:
|
|
|
146
140
|
|
|
147
141
|
### Session Token
|
|
148
142
|
|
|
149
|
-
The Session Token is required for
|
|
143
|
+
The Session Token is required for authentication and must be obtained from your SDK provider.
|
|
150
144
|
|
|
151
145
|
**⚠️ Important Notes:**
|
|
152
146
|
- The Session Token must be valid and not expired
|
|
@@ -204,7 +198,7 @@ import {
|
|
|
204
198
|
const configuration: Configuration = {
|
|
205
199
|
environment: Environment.cn,
|
|
206
200
|
drivingServiceMode: DrivingServiceMode.sdk, // Optional, 'sdk' is default
|
|
207
|
-
// - DrivingServiceMode.sdk: SDK mode - SDK handles
|
|
201
|
+
// - DrivingServiceMode.sdk: SDK mode - SDK handles network communication
|
|
208
202
|
// - DrivingServiceMode.host: Host mode - Host app provides audio and animation data
|
|
209
203
|
logLevel: LogLevel.off, // Optional, 'off' is default
|
|
210
204
|
// - LogLevel.off: Disable all logs
|
|
@@ -233,7 +227,7 @@ const avatar = await avatarManager.load('character-id', (progress) => {
|
|
|
233
227
|
|
|
234
228
|
// 3. Create view (automatically creates Canvas and AvatarController)
|
|
235
229
|
// The playback mode is determined by drivingServiceMode in AvatarSDK configuration
|
|
236
|
-
// - DrivingServiceMode.sdk: SDK mode - SDK handles
|
|
230
|
+
// - DrivingServiceMode.sdk: SDK mode - SDK handles network communication
|
|
237
231
|
// - DrivingServiceMode.host: Host mode - Host app provides audio and animation data
|
|
238
232
|
const container = document.getElementById('avatar-container')
|
|
239
233
|
const avatarView = new AvatarView(avatar, container)
|
|
@@ -290,20 +284,12 @@ This SDK supports two usage modes:
|
|
|
290
284
|
|
|
291
285
|
## 🏗️ Architecture Overview
|
|
292
286
|
|
|
293
|
-
### Three-Layer Architecture
|
|
294
|
-
|
|
295
|
-
The SDK uses a three-layer architecture for clear separation of concerns:
|
|
296
|
-
|
|
297
|
-
1. **Rendering Layer (AvatarView)** - Responsible for 3D rendering only
|
|
298
|
-
2. **Playback Layer (AvatarController)** - Manages audio/animation synchronization and playback
|
|
299
|
-
3. **Network Layer** - Handles WebSocket communication (only in SDK mode, internal implementation)
|
|
300
|
-
|
|
301
287
|
### Core Components
|
|
302
288
|
|
|
303
289
|
- **AvatarSDK** - SDK initialization and management
|
|
304
290
|
- **AvatarManager** - Avatar resource loading and management
|
|
305
|
-
- **AvatarView** - 3D rendering view
|
|
306
|
-
- **AvatarController** - Audio/animation playback controller
|
|
291
|
+
- **AvatarView** - 3D rendering view
|
|
292
|
+
- **AvatarController** - Audio/animation playback controller
|
|
307
293
|
|
|
308
294
|
### Playback Modes
|
|
309
295
|
|
|
@@ -311,7 +297,7 @@ The SDK supports two playback modes, configured in `AvatarSDK.initialize()`:
|
|
|
311
297
|
|
|
312
298
|
#### 1. SDK Mode (Default)
|
|
313
299
|
- Configured via `drivingServiceMode: DrivingServiceMode.sdk` in `AvatarSDK.initialize()`
|
|
314
|
-
- SDK handles
|
|
300
|
+
- SDK handles network communication automatically
|
|
315
301
|
- Send audio data via `AvatarController.send()`
|
|
316
302
|
- SDK receives animation data from backend and synchronizes playback
|
|
317
303
|
- Best for: Real-time audio input scenarios
|
|
@@ -329,33 +315,27 @@ The SDK supports two playback modes, configured in `AvatarSDK.initialize()`:
|
|
|
329
315
|
|
|
330
316
|
The SDK includes a fallback mechanism to ensure audio playback continues even when animation data is unavailable:
|
|
331
317
|
|
|
332
|
-
- **SDK Mode Connection Failure**: If
|
|
333
|
-
- **SDK Mode Server Error**: If the server returns an error after connection is established, the SDK automatically enters audio-only mode for that session
|
|
318
|
+
- **SDK Mode Connection Failure**: If connection fails to establish within 15 seconds, the SDK automatically enters fallback mode. Audio data can still be sent and will play normally, even though no animation data will be received. This ensures audio playback is not interrupted.
|
|
319
|
+
- **SDK Mode Server Error**: If the server returns an error after connection is established, the SDK automatically enters audio-only mode for that session.
|
|
334
320
|
- **Host Mode**: If empty animation data is provided (empty array or undefined), the SDK automatically enters audio-only mode.
|
|
335
321
|
- Once in audio-only mode, any subsequent animation data for that session will be ignored, and only audio will continue playing.
|
|
336
322
|
- The fallback mode is interruptible, just like normal playback mode.
|
|
337
|
-
- Connection state callbacks (`onConnectionState`) will notify you when connection fails or times out
|
|
323
|
+
- Connection state callbacks (`onConnectionState`) will notify you when connection fails or times out.
|
|
338
324
|
|
|
339
325
|
### Data Flow
|
|
340
326
|
|
|
341
327
|
#### SDK Mode Flow
|
|
342
328
|
|
|
343
329
|
```
|
|
344
|
-
|
|
345
|
-
↓
|
|
346
|
-
AvatarController.send()
|
|
347
|
-
↓
|
|
348
|
-
WebSocket → Backend processing
|
|
330
|
+
Audio input (PCM16 mono)
|
|
349
331
|
↓
|
|
350
|
-
|
|
332
|
+
AvatarController.send()
|
|
351
333
|
↓
|
|
352
|
-
|
|
334
|
+
Backend processing → Animation data
|
|
353
335
|
↓
|
|
354
|
-
|
|
336
|
+
SDK synchronizes audio + animation playback
|
|
355
337
|
↓
|
|
356
|
-
|
|
357
|
-
↓
|
|
358
|
-
RenderSystem → WebGPU/WebGL → Canvas rendering
|
|
338
|
+
GPU rendering → Canvas
|
|
359
339
|
```
|
|
360
340
|
|
|
361
341
|
#### Host Mode Flow
|
|
@@ -363,17 +343,12 @@ RenderSystem → WebGPU/WebGL → Canvas rendering
|
|
|
363
343
|
```
|
|
364
344
|
External data source (audio + animation)
|
|
365
345
|
↓
|
|
366
|
-
AvatarController.yieldAudioData(audioChunk)
|
|
367
|
-
|
|
368
|
-
AvatarController.yieldFramesData(keyframesDataArray, conversationId) // keyframesDataArray: (Uint8Array | ArrayBuffer)[] - each element is a protobuf encoded Message
|
|
369
|
-
↓
|
|
370
|
-
AvatarController → AnimationPlayer (synchronized playback)
|
|
346
|
+
AvatarController.yieldAudioData(audioChunk) → returns conversationId
|
|
347
|
+
AvatarController.yieldFramesData(dataArray, conversationId)
|
|
371
348
|
↓
|
|
372
|
-
|
|
349
|
+
SDK synchronizes audio + animation playback
|
|
373
350
|
↓
|
|
374
|
-
|
|
375
|
-
↓
|
|
376
|
-
RenderSystem → WebGPU/WebGL → Canvas rendering
|
|
351
|
+
GPU rendering → Canvas
|
|
377
352
|
```
|
|
378
353
|
|
|
379
354
|
### Audio Format Requirements
|
|
@@ -545,7 +520,7 @@ manager.clearAll()
|
|
|
545
520
|
|
|
546
521
|
### AvatarView
|
|
547
522
|
|
|
548
|
-
3D rendering view
|
|
523
|
+
3D rendering view, responsible for 3D rendering only. Internally automatically creates and manages `AvatarController`.
|
|
549
524
|
|
|
550
525
|
```typescript
|
|
551
526
|
constructor(avatar: Avatar, container: HTMLElement)
|
|
@@ -608,7 +583,7 @@ await currentAvatarView.controller.start()
|
|
|
608
583
|
|
|
609
584
|
### AvatarController
|
|
610
585
|
|
|
611
|
-
Audio/animation playback controller
|
|
586
|
+
Audio/animation playback controller, manages synchronized playback of audio and animation. Automatically handles network communication in SDK mode.
|
|
612
587
|
|
|
613
588
|
**Two Usage Patterns:**
|
|
614
589
|
|
|
@@ -623,9 +598,9 @@ button.addEventListener('click', async () => {
|
|
|
623
598
|
// Initialize audio context - MUST be in user gesture context
|
|
624
599
|
await avatarView.controller.initializeAudioContext()
|
|
625
600
|
|
|
626
|
-
// Start
|
|
601
|
+
// Start service
|
|
627
602
|
await avatarView.controller.start()
|
|
628
|
-
|
|
603
|
+
|
|
629
604
|
// Send audio data (must be mono PCM16 format matching configured sample rate)
|
|
630
605
|
const conversationId = avatarView.controller.send(audioData: ArrayBuffer, end: boolean)
|
|
631
606
|
// Returns: conversationId - Conversation ID for this conversation session
|
|
@@ -633,7 +608,7 @@ button.addEventListener('click', async () => {
|
|
|
633
608
|
// end: true - Mark the end of current conversation round. After end=true, sending new audio data will interrupt any ongoing playback from the previous conversation round
|
|
634
609
|
})
|
|
635
610
|
|
|
636
|
-
// Close
|
|
611
|
+
// Close service
|
|
637
612
|
avatarView.controller.close()
|
|
638
613
|
```
|
|
639
614
|
|
|
@@ -657,7 +632,7 @@ button.addEventListener('click', async () => {
|
|
|
657
632
|
|
|
658
633
|
// Stream animation keyframes (requires conversationId from audio data)
|
|
659
634
|
avatarView.controller.yieldFramesData(
|
|
660
|
-
keyframesDataArray: (Uint8Array | ArrayBuffer)[], // Animation keyframes binary data array
|
|
635
|
+
keyframesDataArray: (Uint8Array | ArrayBuffer)[], // Animation keyframes binary data array
|
|
661
636
|
conversationId: string // Conversation ID (required)
|
|
662
637
|
)
|
|
663
638
|
})
|
|
@@ -702,7 +677,7 @@ const currentVolume = avatarView.controller.getVolume() // Get current volume (
|
|
|
702
677
|
// Set event callbacks
|
|
703
678
|
avatarView.controller.onConnectionState = (state: ConnectionState) => {} // SDK mode only
|
|
704
679
|
avatarView.controller.onConversationState = (state: ConversationState) => {}
|
|
705
|
-
avatarView.controller.onError = (error: Error) => {}
|
|
680
|
+
avatarView.controller.onError = (error: Error) => {} // Usually AvatarError (includes code for SDK/server errors)
|
|
706
681
|
```
|
|
707
682
|
|
|
708
683
|
#### Avatar Transform Methods
|
|
@@ -763,9 +738,9 @@ enum LogLevel {
|
|
|
763
738
|
**Note:** `LogLevel.off` completely disables all logging, including error logs. Use with caution in production environments.
|
|
764
739
|
|
|
765
740
|
**Description:**
|
|
766
|
-
- `environment`: Specifies the environment (cn/intl), SDK will automatically use the corresponding
|
|
741
|
+
- `environment`: Specifies the environment (cn/intl), SDK will automatically use the corresponding server addresses based on the environment
|
|
767
742
|
- `drivingServiceMode`: Specifies the driving service mode
|
|
768
|
-
- `DrivingServiceMode.sdk` (default): SDK mode - SDK handles
|
|
743
|
+
- `DrivingServiceMode.sdk` (default): SDK mode - SDK handles network communication automatically
|
|
769
744
|
- `DrivingServiceMode.host`: Host mode - Host application provides audio and animation data
|
|
770
745
|
- `logLevel`: Controls the verbosity of SDK logs
|
|
771
746
|
- `LogLevel.off` (default): Disable all logs
|
|
@@ -835,12 +810,7 @@ enum ConversationState {
|
|
|
835
810
|
|
|
836
811
|
## 🎨 Rendering System
|
|
837
812
|
|
|
838
|
-
The SDK
|
|
839
|
-
|
|
840
|
-
- **WebGPU** - High-performance rendering for modern browsers
|
|
841
|
-
- **WebGL** - Better compatibility for traditional rendering
|
|
842
|
-
|
|
843
|
-
The rendering system automatically selects the best backend, no manual configuration needed.
|
|
813
|
+
The SDK automatically selects the best rendering backend for your browser, no manual configuration needed.
|
|
844
814
|
|
|
845
815
|
## 🚨 Error Handling
|
|
846
816
|
|
|
@@ -865,12 +835,26 @@ try {
|
|
|
865
835
|
### Error Callbacks
|
|
866
836
|
|
|
867
837
|
```typescript
|
|
838
|
+
import { AvatarError } from '@spatialwalk/avatarkit'
|
|
839
|
+
|
|
868
840
|
avatarView.controller.onError = (error: Error) => {
|
|
869
|
-
|
|
870
|
-
|
|
841
|
+
if (error instanceof AvatarError) {
|
|
842
|
+
console.error('AvatarController error:', error.message, error.code)
|
|
843
|
+
return
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
console.error('AvatarController unknown error:', error)
|
|
871
847
|
}
|
|
872
848
|
```
|
|
873
849
|
|
|
850
|
+
In SDK mode, server `MESSAGE_SERVER_ERROR` is forwarded to `onError` as `AvatarError`:
|
|
851
|
+
- `error.message`: server-returned error message
|
|
852
|
+
- `error.code` mapping:
|
|
853
|
+
- `401` -> `sessionTokenExpired`
|
|
854
|
+
- `400` -> `sessionTokenInvalid`
|
|
855
|
+
- `404` -> `avatarIDUnrecognized`
|
|
856
|
+
- other HTTP status -> original status code string (for example, `"500"`)
|
|
857
|
+
|
|
874
858
|
## 🔄 Resource Management
|
|
875
859
|
|
|
876
860
|
### Lifecycle Management
|
|
@@ -886,7 +870,7 @@ await avatarView.controller.start()
|
|
|
886
870
|
// Use
|
|
887
871
|
avatarView.controller.send(audioData, false)
|
|
888
872
|
|
|
889
|
-
// Cleanup - dispose() automatically cleans up all resources including
|
|
873
|
+
// Cleanup - dispose() automatically cleans up all resources including connections
|
|
890
874
|
avatarView.dispose()
|
|
891
875
|
```
|
|
892
876
|
|
|
@@ -899,26 +883,25 @@ const avatarView = new AvatarView(avatar, container)
|
|
|
899
883
|
|
|
900
884
|
// Use
|
|
901
885
|
const conversationId = avatarView.controller.yieldAudioData(audioChunk, false)
|
|
902
|
-
avatarView.controller.yieldFramesData(keyframesDataArray, conversationId)
|
|
886
|
+
avatarView.controller.yieldFramesData(keyframesDataArray, conversationId)
|
|
903
887
|
|
|
904
888
|
// Cleanup - dispose() automatically cleans up all resources including playback data
|
|
905
889
|
avatarView.dispose()
|
|
906
890
|
```
|
|
907
891
|
|
|
908
|
-
**⚠️ Important Notes:**
|
|
892
|
+
**⚠️ Important Notes:**
|
|
909
893
|
- `dispose()` automatically cleans up all resources, including:
|
|
910
|
-
-
|
|
894
|
+
- Network connections (SDK mode)
|
|
911
895
|
- Playback data and animation resources (both modes)
|
|
912
896
|
- Render system and canvas elements
|
|
913
897
|
- All event listeners and callbacks
|
|
914
898
|
- Not properly calling `dispose()` may cause resource leaks and rendering errors
|
|
915
|
-
- If you need to manually close
|
|
899
|
+
- If you need to manually close connections or clear playback data before disposing, you can call `avatarView.controller.close()` (SDK mode) or `avatarView.controller.clear()` (both modes) first, but it's not required as `dispose()` handles this automatically
|
|
916
900
|
|
|
917
901
|
### Memory Optimization
|
|
918
902
|
|
|
919
|
-
- SDK automatically manages
|
|
903
|
+
- SDK automatically manages memory allocation
|
|
920
904
|
- Supports dynamic loading/unloading of avatar and animation resources
|
|
921
|
-
- Provides memory usage monitoring interface
|
|
922
905
|
|
|
923
906
|
## 🌐 Browser Compatibility
|
|
924
907
|
|
|
@@ -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-CQqZD1OY.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;
|
|
@@ -1,31 +1,18 @@
|
|
|
1
|
-
import { Flame } from '../generated/driveningress/v1/driveningress';
|
|
2
1
|
import { Avatar } from './Avatar';
|
|
3
|
-
import {
|
|
4
|
-
import { AvatarState, ConnectionState, DrivingServiceMode, ConversationState, PostProcessingConfig, KeyframeData } from '../types';
|
|
2
|
+
import { ConnectionState, DrivingServiceMode, ConversationState, PostProcessingConfig, KeyframeData } from '../types';
|
|
5
3
|
export declare class AvatarController {
|
|
6
4
|
private networkLayer?;
|
|
7
5
|
private readonly playbackMode;
|
|
8
|
-
protected avatar: Avatar;
|
|
9
|
-
protected animationPlayer: AnimationPlayer | null;
|
|
10
|
-
protected currentKeyframes: Flame[];
|
|
11
|
-
protected pendingAudioChunks: Array<{
|
|
12
|
-
data: Uint8Array;
|
|
13
|
-
isLast: boolean;
|
|
14
|
-
}>;
|
|
15
|
-
protected isPlaying: boolean;
|
|
16
6
|
private isStartingPlayback;
|
|
17
|
-
protected isConnected: boolean;
|
|
18
|
-
protected currentState: AvatarState;
|
|
19
7
|
private currentConversationId;
|
|
20
8
|
private reqEnd;
|
|
21
|
-
onConnectionState: ((state: ConnectionState) => void) | null;
|
|
9
|
+
onConnectionState: ((state: ConnectionState, error?: Error) => void) | null;
|
|
22
10
|
onConversationState: ((state: ConversationState) => void) | null;
|
|
23
11
|
onError: ((error: Error) => void) | null;
|
|
24
12
|
private eventListeners;
|
|
25
13
|
private renderCallback?;
|
|
26
14
|
private characterHandle;
|
|
27
15
|
private characterId;
|
|
28
|
-
private useGPUPath;
|
|
29
16
|
private postProcessingConfig;
|
|
30
17
|
private playbackLoopId;
|
|
31
18
|
private lastRenderedFrameIndex;
|
|
@@ -34,7 +21,7 @@ export declare class AvatarController {
|
|
|
34
21
|
private readonly KEYFRAMES_CLEANUP_THRESHOLD;
|
|
35
22
|
private lastSyncLogTime;
|
|
36
23
|
private lastOutOfBoundsState;
|
|
37
|
-
private
|
|
24
|
+
private isFallbackMode;
|
|
38
25
|
private playbackStuckCheckState;
|
|
39
26
|
private readonly MAX_AUDIO_TIME_ZERO_COUNT;
|
|
40
27
|
private readonly MAX_AUDIO_TIME_STUCK_COUNT;
|
|
@@ -148,16 +135,4 @@ export declare class AvatarController {
|
|
|
148
135
|
* @returns Current volume value (0.0 - 1.0)
|
|
149
136
|
*/
|
|
150
137
|
getVolume(): number;
|
|
151
|
-
/**
|
|
152
|
-
* Stop playback
|
|
153
|
-
*/
|
|
154
|
-
protected stopPlayback(): void;
|
|
155
|
-
/**
|
|
156
|
-
* Clean up players
|
|
157
|
-
*/
|
|
158
|
-
protected cleanupPlayers(): void;
|
|
159
|
-
/**
|
|
160
|
-
* Emit event
|
|
161
|
-
*/
|
|
162
|
-
protected emit(event: string, data?: any): void;
|
|
163
138
|
}
|
|
@@ -13,16 +13,14 @@ export declare class AvatarView {
|
|
|
13
13
|
private currentKeyframes;
|
|
14
14
|
private lastRenderedFrameIndex;
|
|
15
15
|
private lastRealtimeProtoFrame;
|
|
16
|
-
private
|
|
17
|
-
private
|
|
16
|
+
private renderLoopId;
|
|
17
|
+
private endTransitionFrames;
|
|
18
|
+
private isConversationActive;
|
|
18
19
|
private resizeObserver;
|
|
19
20
|
private onWindowResize;
|
|
20
21
|
private frameCount;
|
|
21
22
|
private lastFpsUpdate;
|
|
22
23
|
private currentFPS;
|
|
23
|
-
private transitionKeyframes;
|
|
24
|
-
private transitionStartTime;
|
|
25
|
-
private readonly startTransitionDurationMs;
|
|
26
24
|
private readonly endTransitionDurationMs;
|
|
27
25
|
private cachedIdleFirstFrame;
|
|
28
26
|
private idleCurrentFrameIndex;
|