@spatialwalk/avatarkit 1.0.0-beta.15 → 1.0.0-beta.17

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +41 -1
  2. package/README.md +124 -78
  3. package/dist/{StreamingAudioPlayer-Dj1w6HZz.js → StreamingAudioPlayer-a8MwHQ3Q.js} +20 -21
  4. package/dist/StreamingAudioPlayer-a8MwHQ3Q.js.map +1 -0
  5. package/dist/audio/StreamingAudioPlayer.d.ts.map +1 -1
  6. package/dist/avatar_core_wasm-BPIbbUx_.js +1664 -0
  7. package/dist/avatar_core_wasm-BPIbbUx_.js.map +1 -0
  8. package/dist/avatar_core_wasm.wasm +0 -0
  9. package/dist/core/AvatarController.d.ts +69 -9
  10. package/dist/core/AvatarController.d.ts.map +1 -1
  11. package/dist/core/AvatarDownloader.d.ts +1 -11
  12. package/dist/core/AvatarDownloader.d.ts.map +1 -1
  13. package/dist/core/AvatarKit.d.ts +5 -0
  14. package/dist/core/AvatarKit.d.ts.map +1 -1
  15. package/dist/core/AvatarManager.d.ts +0 -1
  16. package/dist/core/AvatarManager.d.ts.map +1 -1
  17. package/dist/core/AvatarView.d.ts +22 -6
  18. package/dist/core/AvatarView.d.ts.map +1 -1
  19. package/dist/{index-Bh6KkhoR.js → index-suaZGA5u.js} +1912 -1564
  20. package/dist/index-suaZGA5u.js.map +1 -0
  21. package/dist/index.js +9 -8
  22. package/dist/renderer/webgl/reorderData.d.ts.map +1 -1
  23. package/dist/types/index.d.ts +8 -9
  24. package/dist/types/index.d.ts.map +1 -1
  25. package/dist/vanilla/vite.config.d.ts.map +1 -1
  26. package/dist/wasm/avatarCoreAdapter.d.ts +58 -9
  27. package/dist/wasm/avatarCoreAdapter.d.ts.map +1 -1
  28. package/dist/wasm/avatarCoreMemory.d.ts +5 -1
  29. package/dist/wasm/avatarCoreMemory.d.ts.map +1 -1
  30. package/package.json +4 -2
  31. package/dist/StreamingAudioPlayer-Dj1w6HZz.js.map +0 -1
  32. package/dist/avatar_core_wasm-D-1WNg4B.js +0 -1676
  33. package/dist/avatar_core_wasm-D-1WNg4B.js.map +0 -1
  34. package/dist/index-Bh6KkhoR.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,46 @@ 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.17] - 2025-01-24
9
+
10
+ ### ✨ New Features
11
+ - **Audio-Only Fallback Mechanism** - SDK now includes automatic fallback to audio-only playback when animation data is unavailable
12
+ - Network mode: Automatically enters audio-only mode when server returns an error
13
+ - Host mode: Automatically enters audio-only mode when empty animation data is provided
14
+ - Once in audio-only mode, subsequent animation data for that session is ignored
15
+ - Fallback mode is interruptible, just like normal playback mode
16
+
17
+ ### 🔧 API Changes
18
+ - **Playback Mode Configuration** - Moved playback mode configuration from `AvatarView` constructor to `AvatarKit.initialize()`
19
+ - Playback mode is now determined by `drivingServiceMode` in `AvatarKit.initialize()` configuration
20
+ - `AvatarView` constructor now only requires `avatar` and `container` parameters
21
+ - Removed `AvatarViewOptions` interface
22
+ - `container` parameter is now required (no longer optional)
23
+
24
+ ### 🔧 Improvements
25
+ - Extended transition animation duration from 200ms to 400ms for smoother end-of-playback transitions
26
+
27
+ ### 📚 Documentation
28
+ - Updated README.md to use "SDK mode" and "Host mode" terminology instead of "Network mode" and "External data mode"
29
+ - Added fallback mechanism documentation
30
+ - Updated API reference to reflect new constructor signature
31
+
32
+ ## [1.0.0-beta.16] - 2025-11-21
33
+
34
+ ### ✨ New Features
35
+ - **Multi-AvatarView Support** - SDK now supports multiple `AvatarView` instances simultaneously
36
+ - Each `AvatarView` instance can manage its own character independently
37
+ - Multiple characters can be displayed and controlled at the same time
38
+ - Each instance maintains its own rendering context, playback state, and network connection
39
+ - Removed the previous limitation of "only one AvatarView instance at a time"
40
+
41
+ ### 📚 Documentation
42
+ - Updated README.md to reflect multi-character support capabilities
43
+ - Added multi-character usage examples
44
+ - Removed outdated limitation notes about single AvatarView instance
45
+
46
+ ---
47
+
8
48
  ## [1.0.0-beta.15] - 2025-01-20
9
49
 
10
50
  ### ✨ New Features
@@ -158,7 +198,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
158
198
  - **External Data Mode**:
159
199
  - External components fully control audio and animation data acquisition
160
200
  - SDK only responsible for synchronized playback of externally provided data
161
- - Use `controller.play()`, `controller.sendAudioChunk()` and `controller.sendKeyframes()` methods
201
+ - Use `controller.playback()`, `controller.yieldAudioData()` and `controller.yieldFramesData()` methods
162
202
 
163
203
  ### ✨ New Features
164
204
 
package/README.md CHANGED
@@ -6,6 +6,7 @@ Real-time virtual avatar rendering SDK based on 3D Gaussian Splatting, supportin
6
6
 
7
7
  - **3D Gaussian Splatting Rendering** - Based on the latest point cloud rendering technology, providing high-quality 3D virtual avatars
8
8
  - **Audio-Driven Real-Time Animation Rendering** - Users provide audio data, SDK handles receiving animation data and rendering
9
+ - **Multi-Character Support** - Support multiple avatar instances simultaneously, each with independent state and rendering
9
10
  - **WebGPU/WebGL Dual Rendering Backend** - Automatically selects the best rendering backend for compatibility
10
11
  - **WASM High-Performance Computing** - Uses C++ compiled WebAssembly modules for geometric calculations
11
12
  - **TypeScript Support** - Complete type definitions and IntelliSense
@@ -31,8 +32,13 @@ import {
31
32
  } from '@spatialwalk/avatarkit'
32
33
 
33
34
  // 1. Initialize SDK
35
+ import { DrivingServiceMode } from '@spatialwalk/avatarkit'
36
+
34
37
  const configuration: Configuration = {
35
38
  environment: Environment.test,
39
+ drivingServiceMode: DrivingServiceMode.sdk, // Optional, 'sdk' is default
40
+ // - DrivingServiceMode.sdk: SDK mode (network mode) - SDK handles WebSocket communication
41
+ // - DrivingServiceMode.host: Host mode (external data mode) - Host app provides audio and animation data
36
42
  }
37
43
 
38
44
  await AvatarKit.initialize('your-app-id', configuration)
@@ -47,12 +53,11 @@ const avatar = await avatarManager.load('character-id', (progress) => {
47
53
  })
48
54
 
49
55
  // 3. Create view (automatically creates Canvas and AvatarController)
50
- // Network mode (default)
56
+ // The playback mode is determined by drivingServiceMode in AvatarKit configuration
57
+ // - DrivingServiceMode.sdk: SDK mode (network mode) - SDK handles WebSocket communication
58
+ // - DrivingServiceMode.host: Host mode (external data mode) - Host app provides audio and animation data
51
59
  const container = document.getElementById('avatar-container')
52
- const avatarView = new AvatarView(avatar, {
53
- container: container,
54
- playbackMode: 'network' // Optional, 'network' is default
55
- })
60
+ const avatarView = new AvatarView(avatar, container)
56
61
 
57
62
  // 4. Start real-time communication (network mode only)
58
63
  await avatarView.avatarController.start()
@@ -75,21 +80,26 @@ import { AvatarPlaybackMode } from '@spatialwalk/avatarkit'
75
80
 
76
81
  // 3. Create view with external data mode
77
82
  const container = document.getElementById('avatar-container')
78
- const avatarView = new AvatarView(avatar, {
79
- container: container,
80
- playbackMode: AvatarPlaybackMode.external
81
- })
83
+ const avatarView = new AvatarView(avatar, container)
84
+
85
+ // 4. Start playback with initial data (obtained from your service)
86
+ // Note: Audio and animation data should be obtained from your backend service
87
+ const initialAudioChunks = [{ data: audioData1, isLast: false }, { data: audioData2, isLast: false }]
88
+ const initialKeyframes = animationData1 // Animation keyframes from your service
82
89
 
83
90
  // 4. Start playback with initial data (obtained from your service)
84
91
  // Note: Audio and animation data should be obtained from your backend service
85
92
  const initialAudioChunks = [{ data: audioData1, isLast: false }, { data: audioData2, isLast: false }]
86
93
  const initialKeyframes = animationData1 // Animation keyframes from your service
87
94
 
88
- await avatarView.avatarController.play(initialAudioChunks, initialKeyframes)
95
+ // Step 1: Send audio first to get reqId (required for session management)
96
+ const reqId = await avatarView.avatarController.playback(initialAudioChunks, initialKeyframes)
89
97
 
90
98
  // 5. Stream additional data as needed
91
- avatarView.avatarController.sendAudioChunk(audioData3, false)
92
- avatarView.avatarController.sendKeyframes(animationData2)
99
+ // Important: Always send audio first to get reqId, then use that reqId for animation data
100
+ const currentReqId = avatarView.avatarController.yieldAudioData(audioData3, false)
101
+ // Step 2: Use the reqId to send animation data (mismatched reqId will be discarded)
102
+ avatarView.avatarController.yieldFramesData(animationData2, currentReqId || reqId)
93
103
  ```
94
104
 
95
105
  ### Complete Examples
@@ -121,20 +131,33 @@ The SDK uses a three-layer architecture for clear separation of concerns:
121
131
 
122
132
  ### Playback Modes
123
133
 
124
- The SDK supports two playback modes, configured when creating `AvatarView`:
134
+ The SDK supports two playback modes, configured in `AvatarKit.initialize()`:
125
135
 
126
- #### 1. Network Mode (Default)
136
+ #### 1. SDK Mode (Default)
137
+ - Configured via `drivingServiceMode: DrivingServiceMode.sdk` in `AvatarKit.initialize()`
127
138
  - SDK handles WebSocket communication automatically
128
139
  - Send audio data via `AvatarController.send()`
129
140
  - SDK receives animation data from backend and synchronizes playback
130
141
  - Best for: Real-time audio input scenarios
131
142
 
132
- #### 2. External Data Mode
133
- - External components manage their own network/data fetching
134
- - External components provide both audio and animation data
143
+ #### 2. Host Mode
144
+ - Configured via `drivingServiceMode: DrivingServiceMode.host` in `AvatarKit.initialize()`
145
+ - Host application manages its own network/data fetching
146
+ - Host application provides both audio and animation data
135
147
  - SDK only handles synchronized playback
136
148
  - Best for: Custom data sources, pre-recorded content, or custom network implementations
137
149
 
150
+ **Note:** The playback mode is determined by `drivingServiceMode` in `AvatarKit.initialize()` configuration.
151
+
152
+ ### Fallback Mechanism
153
+
154
+ The SDK includes a fallback mechanism to ensure audio playback continues even when animation data is unavailable:
155
+
156
+ - **Network Mode**: If the server returns an error or fails to provide animation data, the SDK automatically enters audio-only mode and continues playing audio independently
157
+ - **Host Mode**: If empty animation data is provided (empty array or undefined), the SDK automatically enters audio-only mode
158
+ - Once in audio-only mode, any subsequent animation data for that session will be ignored, and only audio will continue playing
159
+ - The fallback mode is interruptible, just like normal playback mode
160
+
138
161
  ### Data Flow
139
162
 
140
163
  #### Network Mode Flow
@@ -162,10 +185,10 @@ RenderSystem → WebGPU/WebGL → Canvas rendering
162
185
  ```
163
186
  External data source (audio + animation)
164
187
 
165
- AvatarController.play(initialAudio, initialKeyframes) // Start playback
188
+ AvatarController.playback(initialAudio, initialKeyframes) // Start playback
166
189
 
167
- AvatarController.sendAudioChunk() // Stream additional audio
168
- AvatarController.sendKeyframes() // Stream additional animation
190
+ AvatarController.yieldAudioData() // Stream additional audio
191
+ AvatarController.yieldFramesData() // Stream additional animation
169
192
 
170
193
  AvatarController → AnimationPlayer (synchronized playback)
171
194
 
@@ -258,8 +281,6 @@ manager.clearCache()
258
281
 
259
282
  3D rendering view (rendering layer), responsible for 3D rendering only. Internally automatically creates and manages `AvatarController`.
260
283
 
261
- **⚠️ Important Limitation:** Currently, the SDK only supports one AvatarView instance at a time. If you need to switch characters, you must first call the `dispose()` method to clean up the current AvatarView, then create a new instance.
262
-
263
284
  **Playback Mode Configuration:**
264
285
  - The playback mode is fixed when creating `AvatarView` and persists throughout its lifecycle
265
286
  - Cannot be changed after creation
@@ -268,18 +289,9 @@ manager.clearCache()
268
289
  import { AvatarPlaybackMode } from '@spatialwalk/avatarkit'
269
290
 
270
291
  // Create view (Canvas is automatically added to container)
271
- // Network mode (default)
292
+ // Create view (playback mode is determined by drivingServiceMode in AvatarKit configuration)
272
293
  const container = document.getElementById('avatar-container')
273
- const avatarView = new AvatarView(avatar: Avatar, {
274
- container: container,
275
- playbackMode: AvatarPlaybackMode.network // Optional, default is 'network'
276
- })
277
-
278
- // External data mode
279
- const avatarView = new AvatarView(avatar: Avatar, {
280
- container: container,
281
- playbackMode: AvatarPlaybackMode.external
282
- })
294
+ const avatarView = new AvatarView(avatar, container)
283
295
 
284
296
  // Get playback mode
285
297
  const mode = avatarView.playbackMode // 'network' | 'external'
@@ -291,24 +303,20 @@ avatarView.dispose()
291
303
  **Character Switching Example:**
292
304
 
293
305
  ```typescript
294
- // Before switching characters, must clean up old AvatarView first
306
+ // To switch characters, simply dispose the old view and create a new one
295
307
  if (currentAvatarView) {
296
308
  currentAvatarView.dispose()
297
- currentAvatarView = null
298
309
  }
299
310
 
300
311
  // Load new character
301
312
  const newAvatar = await avatarManager.load('new-character-id')
302
313
 
303
- // Create new AvatarView (with same or different playback mode)
304
- currentAvatarView = new AvatarView(newAvatar, {
305
- container: container,
306
- playbackMode: AvatarPlaybackMode.network
307
- })
314
+ // Create new AvatarView
315
+ currentAvatarView = new AvatarView(newAvatar, container)
308
316
 
309
317
  // Network mode: start connection
310
318
  if (currentAvatarView.playbackMode === AvatarPlaybackMode.network) {
311
- await currentAvatarView.avatarController.start()
319
+ await currentAvatarView.controller.start()
312
320
  }
313
321
  ```
314
322
 
@@ -341,24 +349,54 @@ avatarView.avatarController.close()
341
349
  #### External Data Mode Methods
342
350
 
343
351
  ```typescript
344
- // Start playback with initial audio and animation data
345
- await avatarView.avatarController.play(
346
- initialAudioChunks?: Array<{ data: Uint8Array, isLast: boolean }>, // Initial audio chunks (16kHz mono PCM16)
347
- initialKeyframes?: any[] // Initial animation keyframes (obtained from your service)
352
+ // Playback existing audio and animation data (starts a new conversation)
353
+ const reqId = await avatarView.avatarController.playback(
354
+ initialAudioChunks?: Array<{ data: Uint8Array, isLast: boolean }>, // Existing audio chunks (16kHz mono PCM16)
355
+ initialKeyframes?: any[] // Existing animation keyframes (obtained from your service)
348
356
  )
357
+ // Returns: reqId - New request ID for this conversation session
349
358
 
350
- // Stream additional audio chunks (after play() is called)
351
- avatarView.avatarController.sendAudioChunk(
359
+ // Stream additional audio chunks (after playback() is called)
360
+ const reqId = avatarView.avatarController.yieldAudioData(
352
361
  data: Uint8Array, // Audio chunk data
353
362
  isLast: boolean = false // Whether this is the last chunk
354
363
  )
364
+ // Returns: reqId - Request ID for this audio session
355
365
 
356
- // Stream additional animation keyframes (after play() is called)
357
- avatarView.avatarController.sendKeyframes(
358
- keyframes: any[] // Additional animation keyframes (obtained from your service)
366
+ // Stream additional animation keyframes (after playback() is called)
367
+ avatarView.avatarController.yieldFramesData(
368
+ keyframes: any[], // Additional animation keyframes (obtained from your service)
369
+ reqId: string // Request ID (required). Use getCurrentReqId() or yieldAudioData() to get reqId.
359
370
  )
360
371
  ```
361
372
 
373
+ **⚠️ Important: Request ID (reqId) Management**
374
+
375
+ For each conversation session, you **must**:
376
+ 1. **First send audio data** to get a reqId:
377
+ - `playback()` returns a reqId when starting a new conversation
378
+ - `yieldAudioData()` returns a reqId for the current audio session
379
+ 2. **Then use that reqId** to send animation data:
380
+ - `yieldFramesData()` requires a valid reqId parameter
381
+ - Animation data with mismatched reqId will be **discarded**
382
+ - Use `getCurrentReqId()` to retrieve the current active reqId
383
+
384
+ **Example Flow:**
385
+ ```typescript
386
+ // Step 1: Send audio first to get reqId
387
+ const reqId = await avatarView.avatarController.playback(initialAudioChunks, initialKeyframes)
388
+ // or
389
+ const reqId = avatarView.avatarController.yieldAudioData(audioChunk, false)
390
+
391
+ // Step 2: Use the reqId to send animation data
392
+ avatarView.avatarController.yieldFramesData(keyframes, reqId)
393
+ ```
394
+
395
+ **Why reqId is required:**
396
+ - Ensures audio and animation data belong to the same conversation session
397
+ - Prevents data from different sessions from being mixed
398
+ - Automatically discards mismatched animation data for data integrity
399
+
362
400
  #### Common Methods (Both Modes)
363
401
 
364
402
  ```typescript
@@ -382,9 +420,10 @@ avatarView.avatarController.onError = (error: Error) => {}
382
420
 
383
421
  **Important Notes:**
384
422
  - `start()` and `close()` are only available in network mode
385
- - `play()`, `sendAudioChunk()`, and `sendKeyframes()` are only available in external data mode
423
+ - `playback()`, `yieldAudioData()`, and `yieldFramesData()` are only available in external data mode
386
424
  - `pause()`, `resume()`, `interrupt()`, and `clear()` are available in both modes
387
425
  - The playback mode is determined when creating `AvatarView` and cannot be changed
426
+ - **Request ID (reqId)**: In external data mode, always send audio data first to obtain a reqId, then use that reqId when sending animation data. Animation data with mismatched reqId will be discarded.
388
427
 
389
428
  ## 🔧 Configuration
390
429
 
@@ -393,11 +432,15 @@ avatarView.avatarController.onError = (error: Error) => {}
393
432
  ```typescript
394
433
  interface Configuration {
395
434
  environment: Environment
435
+ drivingServiceMode?: DrivingServiceMode // Optional, default is 'sdk' (network mode)
396
436
  }
397
437
  ```
398
438
 
399
439
  **Description:**
400
440
  - `environment`: Specifies the environment (cn/us/test), SDK will automatically use the corresponding API address and WebSocket address based on the environment
441
+ - `drivingServiceMode`: Specifies the driving service mode
442
+ - `DrivingServiceMode.sdk` (default): SDK mode - SDK handles WebSocket communication automatically
443
+ - `DrivingServiceMode.host`: Host mode - Host application provides audio and animation data
401
444
  - `sessionToken`: Set separately via `AvatarKit.setSessionToken()`, not in Configuration
402
445
 
403
446
  ```typescript
@@ -408,22 +451,20 @@ enum Environment {
408
451
  }
409
452
  ```
410
453
 
411
- ### AvatarViewOptions
454
+ ### AvatarView Constructor
412
455
 
413
456
  ```typescript
414
- interface AvatarViewOptions {
415
- playbackMode?: AvatarPlaybackMode // Playback mode, default is 'network'
416
- container?: HTMLElement // Canvas container element
417
- }
457
+ constructor(avatar: Avatar, container: HTMLElement)
418
458
  ```
419
459
 
420
- **Description:**
421
- - `playbackMode`: Specifies the playback mode (`'network'` or `'external'`), default is `'network'`
422
- - `'network'`: SDK handles WebSocket communication, send audio via `send()`
423
- - `'external'`: External components provide audio and animation data, SDK handles synchronized playback
424
- - `container`: Optional container element for Canvas, if not provided, Canvas will be created but not added to DOM
425
- - Canvas automatically uses the container's full dimensions (width and height)
426
- - Canvas aspect ratio adapts to container size - set container dimensions to control the aspect ratio
460
+ **Parameters:**
461
+ - `avatar`: Avatar 实例
462
+ - `container`: Canvas 容器元素(必选)
463
+ - Canvas 自动使用容器的完整尺寸(宽度和高度)
464
+ - Canvas 宽高比适应容器尺寸 - 设置容器尺寸以控制宽高比
465
+ - Canvas 会自动添加到容器中
466
+
467
+ **Note:** 播放模式由 `AvatarKit.initialize()` 配置中的 `drivingServiceMode` 决定,而不是在构造函数参数中
427
468
  - SDK automatically handles resize events via ResizeObserver
428
469
 
429
470
  ```typescript
@@ -518,8 +559,7 @@ avatarView.avatarController.onError = (error: Error) => {
518
559
  ```typescript
519
560
  // Initialize
520
561
  const container = document.getElementById('avatar-container')
521
- const avatarView = new AvatarView(avatar, {
522
- container: container,
562
+ const avatarView = new AvatarView(avatar, container)
523
563
  playbackMode: AvatarPlaybackMode.network
524
564
  })
525
565
  await avatarView.avatarController.start()
@@ -537,16 +577,16 @@ avatarView.dispose() // Automatically cleans up all resources
537
577
  ```typescript
538
578
  // Initialize
539
579
  const container = document.getElementById('avatar-container')
540
- const avatarView = new AvatarView(avatar, {
541
- container: container,
542
- playbackMode: AvatarPlaybackMode.external
543
- })
580
+ const avatarView = new AvatarView(avatar, container)
544
581
 
545
582
  // Use
546
583
  const initialAudioChunks = [{ data: audioData1, isLast: false }]
547
- await avatarView.avatarController.play(initialAudioChunks, initialKeyframes)
548
- avatarView.avatarController.sendAudioChunk(audioChunk, false)
549
- avatarView.avatarController.sendKeyframes(keyframes)
584
+ // Step 1: Send audio first to get reqId
585
+ const reqId = await avatarView.avatarController.playback(initialAudioChunks, initialKeyframes)
586
+ // Step 2: Stream additional audio (returns reqId)
587
+ const currentReqId = avatarView.avatarController.yieldAudioData(audioChunk, false)
588
+ // Step 3: Use reqId to send animation data (mismatched reqId will be discarded)
589
+ avatarView.avatarController.yieldFramesData(keyframes, currentReqId || reqId)
550
590
 
551
591
  // Cleanup
552
592
  avatarView.avatarController.clear() // Clear all data and resources
@@ -554,8 +594,7 @@ avatarView.dispose() // Automatically cleans up all resources
554
594
  ```
555
595
 
556
596
  **⚠️ Important Notes:**
557
- - SDK currently only supports one AvatarView instance at a time
558
- - When switching characters, must first call `dispose()` to clean up old AvatarView, then create new instance
597
+ - When disposing AvatarView instances, must call `dispose()` to properly clean up resources
559
598
  - Not properly cleaning up may cause resource leaks and rendering errors
560
599
  - In network mode, call `close()` before `dispose()` to properly close WebSocket connections
561
600
  - In external data mode, call `clear()` before `dispose()` to clear all playback data
@@ -586,7 +625,7 @@ The `send()` method receives audio data in `ArrayBuffer` format:
586
625
 
587
626
  #### External Data Mode
588
627
 
589
- The `play()` method starts playback with initial data, then use `sendAudioChunk()` to stream additional audio:
628
+ The `playback()` method starts playback with existing audio and animation data, generating a new reqId and interrupting any existing conversation. Then use `yieldAudioData()` to stream additional audio:
590
629
 
591
630
  **Audio Format Requirements:**
592
631
  - Same as network mode: 16kHz mono PCM16 format
@@ -594,18 +633,25 @@ The `play()` method starts playback with initial data, then use `sendAudioChunk(
594
633
 
595
634
  **Usage:**
596
635
  ```typescript
597
- // Start playback with initial audio and animation data
636
+ // Playback existing audio and animation data (starts a new conversation)
598
637
  // Note: Audio and animation data should be obtained from your backend service
599
638
  const initialAudioChunks = [
600
639
  { data: audioData1, isLast: false },
601
640
  { data: audioData2, isLast: false }
602
641
  ]
603
- await avatarController.play(initialAudioChunks, initialKeyframes)
642
+ const reqId = await avatarController.playback(initialAudioChunks, initialKeyframes)
643
+ // Returns: reqId - New request ID for this conversation session
604
644
 
605
645
  // Stream additional audio chunks
606
- avatarController.sendAudioChunk(audioChunk, isLast)
646
+ const reqId = avatarController.yieldAudioData(audioChunk, isLast)
647
+ // Returns: reqId - Request ID for this audio session
607
648
  ```
608
649
 
650
+ **⚠️ Request ID Workflow:**
651
+ 1. **Send audio first** → Get reqId from `playback()` or `yieldAudioData()`
652
+ 2. **Send animation with reqId** → Use the reqId from step 1 in `yieldFramesData()`
653
+ 3. **Data matching** → Only animation data with matching reqId will be accepted
654
+
609
655
  **Resampling (Both Modes):**
610
656
  - If your audio source is at a different sample rate (e.g., 24kHz, 48kHz), you **must** resample it to 16kHz before sending
611
657
  - For high-quality resampling, use Web Audio API's `OfflineAudioContext` with anti-aliasing filtering
@@ -1,7 +1,7 @@
1
1
  var C = Object.defineProperty;
2
2
  var g = (h, t, e) => t in h ? C(h, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[t] = e;
3
3
  var i = (h, t, e) => g(h, typeof t != "symbol" ? t + "" : t, e);
4
- import { A as m, e as f, a as c, l as u } from "./index-Bh6KkhoR.js";
4
+ import { A as m, e as f, a as c, l as u } from "./index-suaZGA5u.js";
5
5
  class y {
6
6
  constructor(t) {
7
7
  // AudioContext is managed internally
@@ -30,7 +30,7 @@ class y {
30
30
  i(this, "audioChunks", []);
31
31
  i(this, "scheduledChunks", 0);
32
32
  // Number of chunks already scheduled
33
- i(this, "activeSources", []);
33
+ i(this, "activeSources", /* @__PURE__ */ new Set());
34
34
  // Event callbacks
35
35
  i(this, "onEndedCallback");
36
36
  this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, this.sampleRate = (t == null ? void 0 : t.sampleRate) ?? m.audio.sampleRate, this.channelCount = (t == null ? void 0 : t.channelCount) ?? 1, this.debug = (t == null ? void 0 : t.debug) ?? !1;
@@ -98,7 +98,7 @@ class y {
98
98
  sessionStartTime: this.sessionStartTime,
99
99
  bufferedChunks: this.audioChunks.length,
100
100
  scheduledChunks: this.scheduledChunks,
101
- activeSources: this.activeSources.length
101
+ activeSources: this.activeSources.size
102
102
  }), this.scheduleAllChunks();
103
103
  }
104
104
  /**
@@ -130,7 +130,7 @@ class y {
130
130
  this.scheduledChunks++;
131
131
  return;
132
132
  }
133
- const l = e.data, d = e.isLast, a = this.pcmToAudioBuffer(l);
133
+ const r = e.data, o = e.isLast, a = this.pcmToAudioBuffer(r);
134
134
  if (!a) {
135
135
  u.error("Failed to create AudioBuffer from PCM data"), c.logEvent("character_player", "error", {
136
136
  sessionId: this.sessionId,
@@ -140,15 +140,14 @@ class y {
140
140
  }
141
141
  try {
142
142
  const s = this.audioContext.createBufferSource();
143
- s.buffer = a, s.connect(this.audioContext.destination), s.start(this.scheduledTime), this.activeSources.push(s), s.onended = () => {
144
- const o = this.activeSources.indexOf(s);
145
- o >= 0 && this.activeSources.splice(o, 1), d && this.activeSources.length === 0 && (this.log("Last audio chunk ended, marking playback as ended"), this.markEnded());
143
+ s.buffer = a, s.connect(this.audioContext.destination), s.start(this.scheduledTime), this.activeSources.add(s), s.onended = () => {
144
+ this.activeSources.delete(s), o && this.activeSources.size === 0 && (this.log("Last audio chunk ended, marking playback as ended"), this.markEnded());
146
145
  }, this.scheduledTime += a.duration, this.scheduledChunks++, this.log(`[StreamingAudioPlayer] Scheduled chunk ${t + 1}/${this.audioChunks.length}`, {
147
146
  startTime: this.scheduledTime - a.duration,
148
147
  duration: a.duration,
149
148
  nextScheduleTime: this.scheduledTime,
150
- isLast: d,
151
- activeSources: this.activeSources.length
149
+ isLast: o,
150
+ activeSources: this.activeSources.size
152
151
  });
153
152
  } catch (s) {
154
153
  u.errorWithError("Failed to schedule audio chunk:", s), c.logEvent("character_player", "error", {
@@ -166,25 +165,25 @@ class y {
166
165
  if (!this.audioContext)
167
166
  return null;
168
167
  if (t.length === 0) {
169
- const o = Math.floor(this.sampleRate * 0.01), n = this.audioContext.createBuffer(
168
+ const l = Math.floor(this.sampleRate * 0.01), n = this.audioContext.createBuffer(
170
169
  this.channelCount,
171
- o,
170
+ l,
172
171
  this.sampleRate
173
172
  );
174
- for (let r = 0; r < this.channelCount; r++)
175
- n.getChannelData(r).fill(0);
173
+ for (let d = 0; d < this.channelCount; d++)
174
+ n.getChannelData(d).fill(0);
176
175
  return n;
177
176
  }
178
- const e = new Uint8Array(t), l = new Int16Array(e.buffer, 0, e.length / 2), d = l.length / this.channelCount, a = this.audioContext.createBuffer(
177
+ const e = new Uint8Array(t), r = new Int16Array(e.buffer, 0, e.length / 2), o = r.length / this.channelCount, a = this.audioContext.createBuffer(
179
178
  this.channelCount,
180
- d,
179
+ o,
181
180
  this.sampleRate
182
181
  );
183
182
  for (let s = 0; s < this.channelCount; s++) {
184
- const o = a.getChannelData(s);
185
- for (let n = 0; n < d; n++) {
186
- const r = n * this.channelCount + s;
187
- o[n] = l[r] / 32768;
183
+ const l = a.getChannelData(s);
184
+ for (let n = 0; n < o; n++) {
185
+ const d = n * this.channelCount + s;
186
+ l[n] = r[d] / 32768;
188
187
  }
189
188
  }
190
189
  return a;
@@ -251,7 +250,7 @@ class y {
251
250
  } catch {
252
251
  }
253
252
  }
254
- this.activeSources = [], this.audioChunks = [], this.scheduledChunks = 0, this.log("[StreamingAudioPlayer] Playback stopped, state reset");
253
+ this.activeSources.clear(), this.audioChunks = [], this.scheduledChunks = 0, this.log("[StreamingAudioPlayer] Playback stopped, state reset");
255
254
  }
256
255
  }
257
256
  /**
@@ -332,4 +331,4 @@ class y {
332
331
  export {
333
332
  y as StreamingAudioPlayer
334
333
  };
335
- //# sourceMappingURL=StreamingAudioPlayer-Dj1w6HZz.js.map
334
+ //# sourceMappingURL=StreamingAudioPlayer-a8MwHQ3Q.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StreamingAudioPlayer-a8MwHQ3Q.js","sources":["../audio/StreamingAudioPlayer.ts"],"sourcesContent":["/**\n * Streaming Audio Player\n * Implements real-time audio playback using Web Audio API\n * Supports dynamic PCM chunk addition without Workers\n */\n\nimport { APP_CONFIG } from '../config/app-config'\nimport { AvatarKit } from '../core/AvatarKit'\nimport { errorToMessage } from '../utils/error-utils'\nimport { logger } from '../utils/logger'\n\nexport interface StreamingAudioPlayerOptions {\n sampleRate?: number // PCM sample rate (default: APP_CONFIG.audio.sampleRate, backend requires 16kHz)\n channelCount?: number // Number of channels (default: 1)\n debug?: boolean\n}\n\nexport class StreamingAudioPlayer {\n // AudioContext is managed internally\n private audioContext: AudioContext | null = null\n private sampleRate: number\n private channelCount: number\n private debug: boolean\n\n // Session-level state\n private sessionId: string\n private sessionStartTime = 0 // AudioContext time when session started\n private pausedTimeOffset = 0 // Accumulated paused time\n private pausedAt = 0 // Time when paused\n private pausedAudioContextTime = 0 // audioContext.currentTime when paused (for resume calculation)\n private scheduledTime = 0 // Next chunk schedule time in AudioContext time\n\n // Playback state\n private isPlaying = false\n private isPaused = false\n private autoStartEnabled = true // Control whether to auto-start when buffer is ready\n\n // Audio buffer queue\n private audioChunks: Array<{ data: Uint8Array, isLast: boolean }> = []\n private scheduledChunks = 0 // Number of chunks already scheduled\n private activeSources = new Set<AudioBufferSourceNode>()\n\n // Event callbacks\n private onEndedCallback?: () => void\n\n constructor(options?: StreamingAudioPlayerOptions) {\n this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n this.sampleRate = options?.sampleRate ?? APP_CONFIG.audio.sampleRate\n this.channelCount = options?.channelCount ?? 1\n this.debug = options?.debug ?? false\n }\n\n /**\n * Initialize audio context (create and ensure it's ready)\n */\n async initialize(): Promise<void> {\n if (this.audioContext) {\n return\n }\n\n try {\n // Create AudioContext\n this.audioContext = new AudioContext({\n sampleRate: this.sampleRate,\n })\n\n // Resume context (required for some browsers)\n if (this.audioContext.state === 'suspended') {\n await this.audioContext.resume()\n }\n\n this.log('AudioContext initialized', {\n sessionId: this.sessionId,\n sampleRate: this.audioContext.sampleRate,\n state: this.audioContext.state,\n })\n }\n catch (error) {\n const message = errorToMessage(error)\n AvatarKit.logEvent('activeAudioSessionFailed', 'warning', {\n sessionId: this.sessionId,\n reason: message,\n })\n logger.error('Failed to initialize AudioContext:', message)\n throw error instanceof Error ? error : new Error(message)\n }\n }\n\n /**\n * Add audio chunk (16-bit PCM)\n */\n addChunk(pcmData: Uint8Array, isLast: boolean = false): void {\n if (!this.audioContext) {\n logger.error('AudioContext not initialized')\n return\n }\n\n // Store chunk with metadata\n this.audioChunks.push({ data: pcmData, isLast })\n\n // Track buffer underrun warning\n if (this.isPlaying && this.audioChunks.length === this.scheduledChunks) {\n // Buffer underrun detected - chunks consumed faster than added\n }\n\n this.log(`Added chunk ${this.audioChunks.length}`, {\n size: pcmData.length,\n totalChunks: this.audioChunks.length,\n isLast,\n isPlaying: this.isPlaying,\n scheduledChunks: this.scheduledChunks,\n })\n\n // Auto-start if we have any audio chunks and auto-start is enabled\n if (!this.isPlaying && this.autoStartEnabled && this.audioChunks.length > 0) {\n this.log('[StreamingAudioPlayer] Auto-starting playback from addChunk')\n this.startPlayback()\n }\n // Schedule next chunk if already playing and not paused\n else if (this.isPlaying && !this.isPaused) {\n this.log('[StreamingAudioPlayer] Already playing, scheduling next chunk')\n this.scheduleNextChunk()\n } else {\n this.log('[StreamingAudioPlayer] Not playing and no chunks, waiting for more chunks')\n }\n }\n\n /**\n * Start new session (stop current and start fresh)\n */\n async startNewSession(audioChunks: Array<{ data: Uint8Array, isLast: boolean }>): Promise<void> {\n // Stop current session if playing\n this.stop()\n\n // Generate new session ID to prevent data mixing\n this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`\n\n // Reset session state\n this.audioChunks = []\n this.scheduledChunks = 0\n this.pausedTimeOffset = 0\n this.pausedAt = 0\n this.pausedAudioContextTime = 0\n // Don't set sessionStartTime or scheduledTime here - let startPlayback() set them\n this.log('Starting new session', {\n chunks: audioChunks.length,\n })\n\n // Add audio chunks with their metadata\n for (const chunk of audioChunks) {\n this.addChunk(chunk.data, chunk.isLast)\n }\n }\n\n /**\n * Start playback\n */\n private startPlayback(): void {\n if (!this.audioContext) {\n this.log('[StreamingAudioPlayer] Cannot start playback: AudioContext not initialized')\n return\n }\n if (this.isPlaying) {\n this.log('[StreamingAudioPlayer] Cannot start playback: Already playing')\n return\n }\n\n this.isPlaying = true\n this.sessionStartTime = this.audioContext.currentTime\n this.scheduledTime = this.sessionStartTime\n\n this.log('[StreamingAudioPlayer] Starting playback', {\n sessionStartTime: this.sessionStartTime,\n bufferedChunks: this.audioChunks.length,\n scheduledChunks: this.scheduledChunks,\n activeSources: this.activeSources.size,\n })\n\n // Schedule all available chunks\n this.scheduleAllChunks()\n }\n\n /**\n * Schedule all pending chunks\n */\n private scheduleAllChunks(): void {\n while (this.scheduledChunks < this.audioChunks.length) {\n this.scheduleNextChunk()\n }\n }\n\n /**\n * Schedule next audio chunk\n */\n private scheduleNextChunk(): void {\n if (!this.audioContext) {\n this.log('[StreamingAudioPlayer] Cannot schedule chunk: AudioContext not initialized')\n return\n }\n if (!this.isPlaying || this.isPaused) {\n this.log('[StreamingAudioPlayer] Cannot schedule chunk: Not playing or paused')\n return\n }\n\n const chunkIndex = this.scheduledChunks\n if (chunkIndex >= this.audioChunks.length) {\n this.log(`[StreamingAudioPlayer] No more chunks to schedule (chunkIndex: ${chunkIndex}, totalChunks: ${this.audioChunks.length})`)\n return\n }\n\n const chunk = this.audioChunks[chunkIndex]\n\n // 当音频块为空且不是最后一个块时,跳过调度\n if (chunk.data.length === 0 && !chunk.isLast) {\n this.scheduledChunks++\n return\n }\n\n const pcmData = chunk.data\n const isLast = chunk.isLast\n const audioBuffer = this.pcmToAudioBuffer(pcmData)\n\n if (!audioBuffer) {\n const errorMessage = 'Failed to create AudioBuffer from PCM data'\n logger.error(errorMessage)\n AvatarKit.logEvent('character_player', 'error', {\n sessionId: this.sessionId,\n event: 'audio_buffer_creation_failed',\n })\n return\n }\n\n try {\n // Create and configure source node\n const source = this.audioContext.createBufferSource()\n source.buffer = audioBuffer\n source.connect(this.audioContext.destination)\n\n // Schedule playback\n source.start(this.scheduledTime)\n\n // Track active source for hard-cancel\n this.activeSources.add(source)\n source.onended = () => {\n // Remove from active list when it ends\n this.activeSources.delete(source)\n\n // Check if this was the last chunk and all sources have ended\n if (isLast && this.activeSources.size === 0) {\n this.log('Last audio chunk ended, marking playback as ended')\n this.markEnded()\n }\n }\n\n // Update scheduled time for next chunk\n this.scheduledTime += audioBuffer.duration\n\n this.scheduledChunks++\n\n this.log(`[StreamingAudioPlayer] Scheduled chunk ${chunkIndex + 1}/${this.audioChunks.length}`, {\n startTime: this.scheduledTime - audioBuffer.duration,\n duration: audioBuffer.duration,\n nextScheduleTime: this.scheduledTime,\n isLast,\n activeSources: this.activeSources.size,\n })\n }\n catch (err) {\n logger.errorWithError('Failed to schedule audio chunk:', err)\n AvatarKit.logEvent('character_player', 'error', {\n sessionId: this.sessionId,\n event: 'schedule_chunk_failed',\n reason: err instanceof Error ? err.message : String(err),\n })\n }\n }\n\n /**\n * Convert PCM data to AudioBuffer\n * Input: 16-bit PCM (int16), Output: AudioBuffer (float32 [-1, 1])\n */\n private pcmToAudioBuffer(pcmData: Uint8Array): AudioBuffer | null {\n if (!this.audioContext) {\n return null\n }\n\n // Handle empty PCM data (e.g., when isLast is true)\n if (pcmData.length === 0) {\n // For empty chunks (typically the last chunk), create minimal silence\n // Use a very short duration to avoid playback stuttering\n const silenceDuration = 0.01 // 1ms - minimal silence to maintain timing\n const numSamples = Math.floor(this.sampleRate * silenceDuration)\n\n const audioBuffer = this.audioContext.createBuffer(\n this.channelCount,\n numSamples,\n this.sampleRate,\n )\n\n // Fill with silence (all zeros)\n for (let channel = 0; channel < this.channelCount; channel++) {\n const channelData = audioBuffer.getChannelData(channel)\n channelData.fill(0) // Fill with silence\n }\n\n return audioBuffer\n }\n\n // Create aligned copy to avoid byte offset issues\n // Int16Array requires byteOffset to be a multiple of 2\n const alignedData = new Uint8Array(pcmData)\n const int16Array = new Int16Array(alignedData.buffer, 0, alignedData.length / 2)\n\n // Calculate number of samples\n const numSamples = int16Array.length / this.channelCount\n\n // Create AudioBuffer\n const audioBuffer = this.audioContext.createBuffer(\n this.channelCount,\n numSamples,\n this.sampleRate,\n )\n\n // Convert int16 to float32 [-1, 1]\n for (let channel = 0; channel < this.channelCount; channel++) {\n const channelData = audioBuffer.getChannelData(channel)\n\n for (let i = 0; i < numSamples; i++) {\n const sampleIndex = i * this.channelCount + channel\n // Normalize int16 (-32768 to 32767) to float32 (-1 to 1)\n channelData[i] = int16Array[sampleIndex] / 32768.0\n }\n }\n\n return audioBuffer\n }\n\n /**\n * Get current playback time (seconds)\n */\n getCurrentTime(): number {\n if (!this.audioContext || !this.isPlaying) {\n return 0\n }\n\n if (this.isPaused) {\n return this.pausedAt\n }\n\n // Calculate elapsed time using session start time and paused offset\n const currentAudioTime = this.audioContext.currentTime\n const elapsed = currentAudioTime - this.sessionStartTime - this.pausedTimeOffset\n\n return Math.max(0, elapsed)\n }\n\n /**\n * Pause playback\n */\n pause(): void {\n if (!this.isPlaying || this.isPaused || !this.audioContext) {\n return\n }\n\n // 1. 记录逻辑时间(用于 getCurrentTime 返回固定值)\n this.pausedAt = this.getCurrentTime()\n\n // 2. 记录 AudioContext 时间戳(关键!用于恢复计算)\n this.pausedAudioContextTime = this.audioContext.currentTime\n\n // 3. 设置暂停标志\n this.isPaused = true\n\n // 4. 挂起 AudioContext 以暂停所有活动的音频源\n if (this.audioContext.state === 'running') {\n this.audioContext.suspend().catch((err) => {\n logger.errorWithError('Failed to suspend AudioContext:', err)\n // 如果挂起失败,恢复状态\n this.isPaused = false\n })\n }\n\n this.log('Playback paused', {\n pausedAt: this.pausedAt,\n pausedAudioContextTime: this.pausedAudioContextTime,\n audioContextState: this.audioContext.state,\n })\n }\n\n /**\n * Resume playback\n */\n async resume(): Promise<void> {\n if (!this.isPaused || !this.audioContext || !this.isPlaying) {\n return\n }\n\n // 1. 首先恢复 AudioContext(使 currentTime 继续)\n if (this.audioContext.state === 'suspended') {\n try {\n await this.audioContext.resume()\n }\n catch (err) {\n logger.errorWithError('Failed to resume AudioContext:', err)\n throw err\n }\n }\n\n // 2. 调整 sessionStartTime,使 getCurrentTime() 从 pausedAt 继续\n // 数学推导:\n // 恢复后,我们希望:getCurrentTime() = pausedAt + (currentAudioTime - pausedAudioContextTime)\n // 当前公式:getCurrentTime() = currentAudioTime - sessionStartTime - pausedTimeOffset\n //\n // 令两者相等:\n // pausedAt + (currentAudioTime - pausedAudioContextTime) = currentAudioTime - sessionStartTime - pausedTimeOffset\n // => sessionStartTime = pausedAudioContextTime - pausedAt - pausedTimeOffset\n const currentAudioTime = this.audioContext.currentTime\n this.sessionStartTime = this.pausedAudioContextTime - this.pausedAt - this.pausedTimeOffset\n\n // 3. 清除暂停标志\n this.isPaused = false\n\n // 4. 继续调度未调度的音频块(如果在暂停期间有新数据到达)\n if (this.scheduledChunks < this.audioChunks.length) {\n this.scheduleAllChunks()\n }\n\n this.log('Playback resumed', {\n pausedAt: this.pausedAt,\n pausedAudioContextTime: this.pausedAudioContextTime,\n currentAudioContextTime: currentAudioTime,\n adjustedSessionStartTime: this.sessionStartTime,\n audioContextState: this.audioContext.state,\n })\n }\n\n /**\n * Stop playback\n */\n stop(): void {\n if (!this.audioContext) {\n return\n }\n\n // 如果暂停,先恢复 AudioContext(以便正确停止源)\n if (this.isPaused && this.audioContext.state === 'suspended') {\n this.audioContext.resume().catch(() => {\n // 忽略恢复错误,因为我们要停止播放\n })\n this.isPaused = false\n }\n\n this.isPlaying = false\n this.isPaused = false\n this.sessionStartTime = 0 // Reset session start time\n this.scheduledTime = 0 // Reset scheduled time for next session\n\n // Hard stop all scheduled sources immediately\n for (const source of this.activeSources) {\n source.onended = null\n try {\n source.stop(0)\n }\n catch {}\n try {\n source.disconnect()\n }\n catch {}\n }\n this.activeSources.clear()\n\n // 清理音频块和调度状态,确保下次播放时状态干净\n this.audioChunks = []\n this.scheduledChunks = 0\n\n this.log('[StreamingAudioPlayer] Playback stopped, state reset')\n\n // Note: Individual source nodes will stop automatically\n // We just reset our state\n }\n\n /**\n * Enable or disable auto-start (for delayed start scenarios)\n */\n setAutoStart(enabled: boolean): void {\n this.autoStartEnabled = enabled\n this.log(`Auto-start ${enabled ? 'enabled' : 'disabled'}`)\n }\n\n /**\n * Start playback manually (for delayed start scenarios)\n * This allows starting playback after transition animation completes\n */\n play(): void {\n if (this.isPlaying) {\n return\n }\n // Enable auto-start when manually starting playback\n this.autoStartEnabled = true\n this.startPlayback()\n }\n\n /**\n * Mark playback as ended\n */\n markEnded(): void {\n this.log('Playback ended')\n this.isPlaying = false\n this.onEndedCallback?.()\n }\n\n /**\n * Set ended callback\n */\n onEnded(callback: () => void): void {\n this.onEndedCallback = callback\n }\n\n /**\n * Check if playing\n */\n isPlayingNow(): boolean {\n return this.isPlaying && !this.isPaused\n }\n\n /**\n * Get total duration of buffered audio\n */\n getBufferedDuration(): number {\n if (!this.audioContext) {\n return 0\n }\n\n let totalSamples = 0\n for (const chunk of this.audioChunks) {\n totalSamples += chunk.data.length / 2 / this.channelCount // 16-bit = 2 bytes per sample\n }\n\n return totalSamples / this.sampleRate\n }\n\n /**\n * Get remaining duration (buffered - played) in seconds\n */\n getRemainingDuration(): number {\n const total = this.getBufferedDuration()\n const played = this.getCurrentTime()\n return Math.max(0, total - played)\n }\n\n\n /**\n * Dispose and cleanup\n */\n dispose(): void {\n this.stop()\n\n // Close AudioContext\n if (this.audioContext) {\n this.audioContext.close()\n this.audioContext = null\n }\n\n // Clear session state\n this.audioChunks = []\n this.scheduledChunks = 0\n this.sessionStartTime = 0\n this.pausedTimeOffset = 0\n this.pausedAt = 0\n this.pausedAudioContextTime = 0\n this.scheduledTime = 0\n this.onEndedCallback = undefined\n\n this.log('StreamingAudioPlayer disposed')\n }\n\n /**\n * Flush buffered audio\n * - hard: stops all playing sources and clears all chunks\n * - soft (default): clears UNSCHEDULED chunks only\n */\n flush(options?: { hard?: boolean }): void {\n const hard = options?.hard === true\n if (hard) {\n this.stop()\n this.audioChunks = []\n this.scheduledChunks = 0\n this.sessionStartTime = 0\n this.pausedAt = 0\n this.scheduledTime = 0\n this.log('Flushed (hard)')\n return\n }\n\n // Soft flush: drop unscheduled region\n if (this.scheduledChunks < this.audioChunks.length) {\n this.audioChunks.splice(this.scheduledChunks)\n }\n this.log('Flushed (soft)', { remainingScheduled: this.scheduledChunks })\n }\n\n /**\n * Debug logging\n */\n private log(message: string, data?: unknown): void {\n if (this.debug) {\n logger.log(`[StreamingAudioPlayer] ${message}`, data || '')\n }\n }\n}\n"],"names":["StreamingAudioPlayer","options","__publicField","APP_CONFIG","error","message","errorToMessage","AvatarKit","logger","pcmData","isLast","audioChunks","chunk","chunkIndex","audioBuffer","source","err","numSamples","channel","alignedData","int16Array","channelData","i","sampleIndex","elapsed","currentAudioTime","enabled","_a","callback","totalSamples","total","played","data"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"StreamingAudioPlayer.d.ts","sourceRoot":"","sources":["../../audio/StreamingAudioPlayer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,WAAW,2BAA2B;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,qBAAa,oBAAoB;IAE/B,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,KAAK,CAAS;IAGtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,sBAAsB,CAAI;IAClC,OAAO,CAAC,aAAa,CAAI;IAGzB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,gBAAgB,CAAO;IAG/B,OAAO,CAAC,WAAW,CAAmD;IACtE,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,aAAa,CAA8B;IAGnD,OAAO,CAAC,eAAe,CAAC,CAAY;gBAExB,OAAO,CAAC,EAAE,2BAA2B;IAOjD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiCjC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,GAAE,OAAe,GAAG,IAAI;IAoC5D;;OAEG;IACG,eAAe,CAAC,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB/F;;OAEG;IACH,OAAO,CAAC,aAAa;IAyBrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAqFzB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAwDxB;;OAEG;IACH,cAAc,IAAI,MAAM;IAgBxB;;OAEG;IACH,KAAK,IAAI,IAAI;IA8Bb;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IA4C7B;;OAEG;IACH,IAAI,IAAI,IAAI;IA0CZ;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAKpC;;;OAGG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACH,SAAS,IAAI,IAAI;IAMjB;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAInC;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,mBAAmB,IAAI,MAAM;IAa7B;;OAEG;IACH,oBAAoB,IAAI,MAAM;IAO9B;;OAEG;IACH,OAAO,IAAI,IAAI;IAsBf;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAoBzC;;OAEG;IACH,OAAO,CAAC,GAAG;CAKZ"}
1
+ {"version":3,"file":"StreamingAudioPlayer.d.ts","sourceRoot":"","sources":["../../audio/StreamingAudioPlayer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,MAAM,WAAW,2BAA2B;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,qBAAa,oBAAoB;IAE/B,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,YAAY,CAAQ;IAC5B,OAAO,CAAC,KAAK,CAAS;IAGtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,gBAAgB,CAAI;IAC5B,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,sBAAsB,CAAI;IAClC,OAAO,CAAC,aAAa,CAAI;IAGzB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,gBAAgB,CAAO;IAG/B,OAAO,CAAC,WAAW,CAAmD;IACtE,OAAO,CAAC,eAAe,CAAI;IAC3B,OAAO,CAAC,aAAa,CAAmC;IAGxD,OAAO,CAAC,eAAe,CAAC,CAAY;gBAExB,OAAO,CAAC,EAAE,2BAA2B;IAOjD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiCjC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,GAAE,OAAe,GAAG,IAAI;IAoC5D;;OAEG;IACG,eAAe,CAAC,WAAW,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB/F;;OAEG;IACH,OAAO,CAAC,aAAa;IAyBrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAmFzB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAwDxB;;OAEG;IACH,cAAc,IAAI,MAAM;IAgBxB;;OAEG;IACH,KAAK,IAAI,IAAI;IA8Bb;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IA4C7B;;OAEG;IACH,IAAI,IAAI,IAAI;IA0CZ;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAKpC;;;OAGG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACH,SAAS,IAAI,IAAI;IAMjB;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAInC;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,mBAAmB,IAAI,MAAM;IAa7B;;OAEG;IACH,oBAAoB,IAAI,MAAM;IAO9B;;OAEG;IACH,OAAO,IAAI,IAAI;IAsBf;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAoBzC;;OAEG;IACH,OAAO,CAAC,GAAG;CAKZ"}