@spatialwalk/avatarkit 1.0.0-beta.21 → 1.0.0-beta.22
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 +18 -2
- package/README.md +26 -23
- package/dist/{StreamingAudioPlayer-DEXcuhRW.js → StreamingAudioPlayer-DjPD7f6u.js} +61 -42
- package/dist/StreamingAudioPlayer-DjPD7f6u.js.map +1 -0
- package/dist/audio/AnimationPlayer.d.ts +11 -0
- package/dist/audio/AnimationPlayer.d.ts.map +1 -1
- package/dist/audio/StreamingAudioPlayer.d.ts +13 -0
- package/dist/audio/StreamingAudioPlayer.d.ts.map +1 -1
- package/dist/core/AvatarController.d.ts +22 -12
- package/dist/core/AvatarController.d.ts.map +1 -1
- package/dist/core/AvatarKit.d.ts.map +1 -1
- package/dist/core/AvatarView.d.ts +0 -6
- package/dist/core/AvatarView.d.ts.map +1 -1
- package/dist/{index-ChKhyUK4.js → index-BB9yoGY2.js} +474 -429
- package/dist/index-BB9yoGY2.js.map +1 -0
- package/dist/index.js +11 -11
- package/dist/types/index.d.ts +10 -19
- package/dist/types/index.d.ts.map +1 -1
- package/dist/vanilla/vite.config.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/StreamingAudioPlayer-DEXcuhRW.js.map +0 -1
- package/dist/index-ChKhyUK4.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +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.22] - 2025-01-26
|
|
9
|
+
|
|
10
|
+
### 🔧 API Changes
|
|
11
|
+
- **State Callback Renamed** - `onAvatarState` has been renamed to `onConversationState` for better clarity
|
|
12
|
+
- The callback now uses `ConversationState` enum with states: `idle` and `playing`
|
|
13
|
+
- **Environment Enum Updated** - `Environment.us` has been renamed to `Environment.intl` for better internationalization support
|
|
14
|
+
- All references to `Environment.us` should be updated to `Environment.intl`
|
|
15
|
+
- Remote config endpoints now use `intl` instead of `us`
|
|
16
|
+
|
|
17
|
+
### ✨ New Features
|
|
18
|
+
- **Volume Control** - Added volume control API for audio playback
|
|
19
|
+
- `setVolume(volume: number)` - Set audio volume (0.0 to 1.0)
|
|
20
|
+
- `getVolume(): number` - Get current audio volume
|
|
21
|
+
- Volume control only affects the avatar's audio player, not system volume
|
|
22
|
+
- Volume changes take effect immediately, including for currently playing audio
|
|
23
|
+
|
|
8
24
|
## [1.0.0-beta.21] - 2025-01-25
|
|
9
25
|
|
|
10
26
|
### ✨ New Features
|
|
@@ -221,7 +237,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
221
237
|
## [1.0.0-beta.5] - 2025-11-14
|
|
222
238
|
|
|
223
239
|
### 🐛 Bug Fixes
|
|
224
|
-
- Fixed missing `
|
|
240
|
+
- Fixed missing `DrivingServiceMode` enum export in published package
|
|
225
241
|
|
|
226
242
|
---
|
|
227
243
|
|
|
@@ -286,7 +302,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
286
302
|
// New API
|
|
287
303
|
new AvatarView(avatar, {
|
|
288
304
|
container: container,
|
|
289
|
-
playbackMode:
|
|
305
|
+
playbackMode: DrivingServiceMode.sdk // or DrivingServiceMode.host
|
|
290
306
|
})
|
|
291
307
|
```
|
|
292
308
|
|
package/README.md
CHANGED
|
@@ -32,7 +32,6 @@ import {
|
|
|
32
32
|
} from '@spatialwalk/avatarkit'
|
|
33
33
|
|
|
34
34
|
// 1. Initialize SDK
|
|
35
|
-
import { DrivingServiceMode } from '@spatialwalk/avatarkit'
|
|
36
35
|
|
|
37
36
|
const configuration: Configuration = {
|
|
38
37
|
environment: Environment.test,
|
|
@@ -74,7 +73,6 @@ avatarView.avatarController.send(audioData, true) // end=true marks the end of c
|
|
|
74
73
|
### Host Mode Example
|
|
75
74
|
|
|
76
75
|
```typescript
|
|
77
|
-
import { AvatarPlaybackMode } from '@spatialwalk/avatarkit'
|
|
78
76
|
|
|
79
77
|
// 1-3. Same as SDK mode (initialize SDK, load character)
|
|
80
78
|
|
|
@@ -293,16 +291,12 @@ manager.clearCache()
|
|
|
293
291
|
- Cannot be changed after creation
|
|
294
292
|
|
|
295
293
|
```typescript
|
|
296
|
-
import { AvatarPlaybackMode } from '@spatialwalk/avatarkit'
|
|
297
294
|
|
|
298
295
|
// Create view (Canvas is automatically added to container)
|
|
299
296
|
// Create view (playback mode is determined by drivingServiceMode in AvatarKit configuration)
|
|
300
297
|
const container = document.getElementById('avatar-container')
|
|
301
298
|
const avatarView = new AvatarView(avatar, container)
|
|
302
299
|
|
|
303
|
-
// Get playback mode
|
|
304
|
-
const mode = avatarView.playbackMode // 'network' | 'external'
|
|
305
|
-
|
|
306
300
|
// Wait for first frame to render
|
|
307
301
|
await avatarView.ready // Promise that resolves when the first frame is rendered
|
|
308
302
|
|
|
@@ -324,10 +318,8 @@ const newAvatar = await avatarManager.load('new-character-id')
|
|
|
324
318
|
// Create new AvatarView
|
|
325
319
|
currentAvatarView = new AvatarView(newAvatar, container)
|
|
326
320
|
|
|
327
|
-
// SDK mode: start connection
|
|
328
|
-
|
|
329
|
-
await currentAvatarView.controller.start()
|
|
330
|
-
}
|
|
321
|
+
// SDK mode: start connection (will throw error if not in SDK mode)
|
|
322
|
+
await currentAvatarView.controller.start()
|
|
331
323
|
```
|
|
332
324
|
|
|
333
325
|
### AvatarController
|
|
@@ -435,18 +427,23 @@ avatarView.avatarController.clear()
|
|
|
435
427
|
const conversationId = avatarView.avatarController.getCurrentConversationId()
|
|
436
428
|
// Returns: Current conversationId for the active audio session, or null if no active session
|
|
437
429
|
|
|
430
|
+
// Volume control (affects only avatar audio player, not system volume)
|
|
431
|
+
avatarView.avatarController.setVolume(0.5) // Set volume to 50% (0.0 to 1.0)
|
|
432
|
+
const currentVolume = avatarView.avatarController.getVolume() // Get current volume (0.0 to 1.0)
|
|
433
|
+
|
|
438
434
|
// Set event callbacks
|
|
439
435
|
avatarView.avatarController.onConnectionState = (state: ConnectionState) => {} // SDK mode only
|
|
440
|
-
avatarView.avatarController.
|
|
436
|
+
avatarView.avatarController.onConversationState = (state: ConversationState) => {}
|
|
441
437
|
avatarView.avatarController.onError = (error: Error) => {}
|
|
442
438
|
```
|
|
443
439
|
|
|
444
440
|
**Important Notes:**
|
|
445
441
|
- `start()` and `close()` are only available in SDK mode
|
|
446
442
|
- `playback()`, `yieldAudioData()`, and `yieldFramesData()` are only available in Host mode
|
|
447
|
-
- `pause()`, `resume()`, `interrupt()`, `clear()`, and `
|
|
443
|
+
- `pause()`, `resume()`, `interrupt()`, `clear()`, `getCurrentConversationId()`, `setVolume()`, and `getVolume()` are available in both modes
|
|
448
444
|
- The playback mode is determined when creating `AvatarView` and cannot be changed
|
|
449
445
|
- **Conversation ID**: In Host mode, always send audio data first to obtain a conversationId, then use that conversationId when sending animation data. Animation data with mismatched conversationId will be discarded. Use `getCurrentConversationId()` to retrieve the current active conversationId.
|
|
446
|
+
- **Volume Control**: Use `setVolume(volume)` to control audio playback volume (0.0 to 1.0). This only affects the avatar's audio player, not system volume. Volume changes take effect immediately, including for currently playing audio.
|
|
450
447
|
|
|
451
448
|
## 🔧 Configuration
|
|
452
449
|
|
|
@@ -460,7 +457,7 @@ interface Configuration {
|
|
|
460
457
|
```
|
|
461
458
|
|
|
462
459
|
**Description:**
|
|
463
|
-
- `environment`: Specifies the environment (cn/
|
|
460
|
+
- `environment`: Specifies the environment (cn/intl/test), SDK will automatically use the corresponding API address and WebSocket address based on the environment
|
|
464
461
|
- `drivingServiceMode`: Specifies the driving service mode
|
|
465
462
|
- `DrivingServiceMode.sdk` (default): SDK mode - SDK handles WebSocket communication automatically
|
|
466
463
|
- `DrivingServiceMode.host`: Host mode - Host application provides audio and animation data
|
|
@@ -469,7 +466,7 @@ interface Configuration {
|
|
|
469
466
|
```typescript
|
|
470
467
|
enum Environment {
|
|
471
468
|
cn = 'cn', // China region
|
|
472
|
-
|
|
469
|
+
intl = 'intl', // International region
|
|
473
470
|
test = 'test' // Test environment
|
|
474
471
|
}
|
|
475
472
|
```
|
|
@@ -491,9 +488,9 @@ constructor(avatar: Avatar, container: HTMLElement)
|
|
|
491
488
|
- SDK automatically handles resize events via ResizeObserver
|
|
492
489
|
|
|
493
490
|
```typescript
|
|
494
|
-
enum
|
|
495
|
-
|
|
496
|
-
|
|
491
|
+
enum DrivingServiceMode {
|
|
492
|
+
sdk = 'sdk', // SDK mode: SDK handles WebSocket communication
|
|
493
|
+
host = 'host' // Host mode: Host provides data, SDK handles playback
|
|
497
494
|
}
|
|
498
495
|
```
|
|
499
496
|
|
|
@@ -524,17 +521,23 @@ enum ConnectionState {
|
|
|
524
521
|
}
|
|
525
522
|
```
|
|
526
523
|
|
|
527
|
-
###
|
|
524
|
+
### ConversationState
|
|
528
525
|
|
|
529
526
|
```typescript
|
|
530
|
-
enum
|
|
531
|
-
idle = 'idle', //
|
|
532
|
-
|
|
533
|
-
playing = 'playing', // Playing
|
|
534
|
-
paused = 'paused' // Paused (can be resumed)
|
|
527
|
+
enum ConversationState {
|
|
528
|
+
idle = 'idle', // 呼吸态
|
|
529
|
+
playing = 'playing' // 播放态
|
|
535
530
|
}
|
|
536
531
|
```
|
|
537
532
|
|
|
533
|
+
**状态说明:**
|
|
534
|
+
- `idle`: 数字人处于呼吸态,等待对话开始
|
|
535
|
+
- `playing`: 数字人正在播放对话内容(包括过渡动画期间)
|
|
536
|
+
|
|
537
|
+
**注意:** 过渡动画期间会提前通知目标状态:
|
|
538
|
+
- 从 `idle` 过渡到 `playing` 时,立即通知 `playing` 状态
|
|
539
|
+
- 从 `playing` 过渡到 `idle` 时,立即通知 `idle` 状态
|
|
540
|
+
|
|
538
541
|
## 🎨 Rendering System
|
|
539
542
|
|
|
540
543
|
The SDK supports two rendering backends:
|
|
@@ -1,38 +1,42 @@
|
|
|
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
|
-
var
|
|
4
|
-
import { A as m, e as f, a as c, l as
|
|
3
|
+
var s = (h, t, e) => g(h, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
import { A as m, e as f, a as c, l as n } from "./index-BB9yoGY2.js";
|
|
5
5
|
class y {
|
|
6
6
|
constructor(t) {
|
|
7
7
|
// AudioContext is managed internally
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
s(this, "audioContext", null);
|
|
9
|
+
s(this, "sampleRate");
|
|
10
|
+
s(this, "channelCount");
|
|
11
|
+
s(this, "debug");
|
|
12
12
|
// Session-level state
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
s(this, "sessionId");
|
|
14
|
+
s(this, "sessionStartTime", 0);
|
|
15
15
|
// AudioContext time when session started
|
|
16
|
-
|
|
16
|
+
s(this, "pausedTimeOffset", 0);
|
|
17
17
|
// Accumulated paused time
|
|
18
|
-
|
|
18
|
+
s(this, "pausedAt", 0);
|
|
19
19
|
// Time when paused
|
|
20
|
-
|
|
20
|
+
s(this, "pausedAudioContextTime", 0);
|
|
21
21
|
// audioContext.currentTime when paused (for resume calculation)
|
|
22
|
-
|
|
22
|
+
s(this, "scheduledTime", 0);
|
|
23
23
|
// Next chunk schedule time in AudioContext time
|
|
24
24
|
// Playback state
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
s(this, "isPlaying", !1);
|
|
26
|
+
s(this, "isPaused", !1);
|
|
27
|
+
s(this, "autoStartEnabled", !0);
|
|
28
28
|
// Control whether to auto-start when buffer is ready
|
|
29
29
|
// Audio buffer queue
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
s(this, "audioChunks", []);
|
|
31
|
+
s(this, "scheduledChunks", 0);
|
|
32
32
|
// Number of chunks already scheduled
|
|
33
|
-
|
|
33
|
+
s(this, "activeSources", /* @__PURE__ */ new Set());
|
|
34
|
+
// Volume control
|
|
35
|
+
s(this, "gainNode", null);
|
|
36
|
+
s(this, "volume", 1);
|
|
37
|
+
// Default volume 1.0 (0.0 - 1.0)
|
|
34
38
|
// Event callbacks
|
|
35
|
-
|
|
39
|
+
s(this, "onEndedCallback");
|
|
36
40
|
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;
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
@@ -43,7 +47,7 @@ class y {
|
|
|
43
47
|
try {
|
|
44
48
|
this.audioContext = new AudioContext({
|
|
45
49
|
sampleRate: this.sampleRate
|
|
46
|
-
}), this.audioContext.state === "suspended" && await this.audioContext.resume(), this.log("AudioContext initialized", {
|
|
50
|
+
}), this.gainNode = this.audioContext.createGain(), this.gainNode.gain.value = this.volume, this.gainNode.connect(this.audioContext.destination), this.audioContext.state === "suspended" && await this.audioContext.resume(), this.log("AudioContext initialized", {
|
|
47
51
|
sessionId: this.sessionId,
|
|
48
52
|
sampleRate: this.audioContext.sampleRate,
|
|
49
53
|
state: this.audioContext.state
|
|
@@ -53,7 +57,7 @@ class y {
|
|
|
53
57
|
throw c.logEvent("activeAudioSessionFailed", "warning", {
|
|
54
58
|
sessionId: this.sessionId,
|
|
55
59
|
reason: e
|
|
56
|
-
}),
|
|
60
|
+
}), n.error("Failed to initialize AudioContext:", e), t instanceof Error ? t : new Error(e);
|
|
57
61
|
}
|
|
58
62
|
}
|
|
59
63
|
/**
|
|
@@ -61,7 +65,7 @@ class y {
|
|
|
61
65
|
*/
|
|
62
66
|
addChunk(t, e = !1) {
|
|
63
67
|
if (!this.audioContext) {
|
|
64
|
-
|
|
68
|
+
n.error("AudioContext not initialized");
|
|
65
69
|
return;
|
|
66
70
|
}
|
|
67
71
|
this.audioChunks.push({ data: t, isLast: e }), this.log(`Added chunk ${this.audioChunks.length}`, {
|
|
@@ -132,16 +136,16 @@ class y {
|
|
|
132
136
|
}
|
|
133
137
|
const r = e.data, o = e.isLast, a = this.pcmToAudioBuffer(r);
|
|
134
138
|
if (!a) {
|
|
135
|
-
|
|
139
|
+
n.error("Failed to create AudioBuffer from PCM data"), c.logEvent("character_player", "error", {
|
|
136
140
|
sessionId: this.sessionId,
|
|
137
141
|
event: "audio_buffer_creation_failed"
|
|
138
142
|
});
|
|
139
143
|
return;
|
|
140
144
|
}
|
|
141
145
|
try {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
this.activeSources.delete(
|
|
146
|
+
const i = this.audioContext.createBufferSource();
|
|
147
|
+
i.buffer = a, i.connect(this.gainNode), i.start(this.scheduledTime), this.activeSources.add(i), i.onended = () => {
|
|
148
|
+
this.activeSources.delete(i), o && this.activeSources.size === 0 && (this.log("Last audio chunk ended, marking playback as ended"), this.markEnded());
|
|
145
149
|
}, this.scheduledTime += a.duration, this.scheduledChunks++, this.log(`[StreamingAudioPlayer] Scheduled chunk ${t + 1}/${this.audioChunks.length}`, {
|
|
146
150
|
startTime: this.scheduledTime - a.duration,
|
|
147
151
|
duration: a.duration,
|
|
@@ -149,11 +153,11 @@ class y {
|
|
|
149
153
|
isLast: o,
|
|
150
154
|
activeSources: this.activeSources.size
|
|
151
155
|
});
|
|
152
|
-
} catch (
|
|
153
|
-
|
|
156
|
+
} catch (i) {
|
|
157
|
+
n.errorWithError("Failed to schedule audio chunk:", i), c.logEvent("character_player", "error", {
|
|
154
158
|
sessionId: this.sessionId,
|
|
155
159
|
event: "schedule_chunk_failed",
|
|
156
|
-
reason:
|
|
160
|
+
reason: i instanceof Error ? i.message : String(i)
|
|
157
161
|
});
|
|
158
162
|
}
|
|
159
163
|
}
|
|
@@ -165,25 +169,25 @@ class y {
|
|
|
165
169
|
if (!this.audioContext)
|
|
166
170
|
return null;
|
|
167
171
|
if (t.length === 0) {
|
|
168
|
-
const l = Math.floor(this.sampleRate * 0.01),
|
|
172
|
+
const l = Math.floor(this.sampleRate * 0.01), u = this.audioContext.createBuffer(
|
|
169
173
|
this.channelCount,
|
|
170
174
|
l,
|
|
171
175
|
this.sampleRate
|
|
172
176
|
);
|
|
173
177
|
for (let d = 0; d < this.channelCount; d++)
|
|
174
|
-
|
|
175
|
-
return
|
|
178
|
+
u.getChannelData(d).fill(0);
|
|
179
|
+
return u;
|
|
176
180
|
}
|
|
177
181
|
const e = new Uint8Array(t), r = new Int16Array(e.buffer, 0, e.length / 2), o = r.length / this.channelCount, a = this.audioContext.createBuffer(
|
|
178
182
|
this.channelCount,
|
|
179
183
|
o,
|
|
180
184
|
this.sampleRate
|
|
181
185
|
);
|
|
182
|
-
for (let
|
|
183
|
-
const l = a.getChannelData(
|
|
184
|
-
for (let
|
|
185
|
-
const d =
|
|
186
|
-
l[
|
|
186
|
+
for (let i = 0; i < this.channelCount; i++) {
|
|
187
|
+
const l = a.getChannelData(i);
|
|
188
|
+
for (let u = 0; u < o; u++) {
|
|
189
|
+
const d = u * this.channelCount + i;
|
|
190
|
+
l[u] = r[d] / 32768;
|
|
187
191
|
}
|
|
188
192
|
}
|
|
189
193
|
return a;
|
|
@@ -204,7 +208,7 @@ class y {
|
|
|
204
208
|
*/
|
|
205
209
|
pause() {
|
|
206
210
|
!this.isPlaying || this.isPaused || !this.audioContext || (this.pausedAt = this.getCurrentTime(), this.pausedAudioContextTime = this.audioContext.currentTime, this.isPaused = !0, this.audioContext.state === "running" && this.audioContext.suspend().catch((t) => {
|
|
207
|
-
|
|
211
|
+
n.errorWithError("Failed to suspend AudioContext:", t), this.isPaused = !1;
|
|
208
212
|
}), this.log("Playback paused", {
|
|
209
213
|
pausedAt: this.pausedAt,
|
|
210
214
|
pausedAudioContextTime: this.pausedAudioContextTime,
|
|
@@ -221,7 +225,7 @@ class y {
|
|
|
221
225
|
try {
|
|
222
226
|
await this.audioContext.resume();
|
|
223
227
|
} catch (e) {
|
|
224
|
-
throw
|
|
228
|
+
throw n.errorWithError("Failed to resume AudioContext:", e), e;
|
|
225
229
|
}
|
|
226
230
|
const t = this.audioContext.currentTime;
|
|
227
231
|
this.sessionStartTime = this.pausedAudioContextTime - this.pausedAt - this.pausedTimeOffset, this.isPaused = !1, this.scheduledChunks < this.audioChunks.length && this.scheduleAllChunks(), this.log("Playback resumed", {
|
|
@@ -307,7 +311,7 @@ class y {
|
|
|
307
311
|
* Dispose and cleanup
|
|
308
312
|
*/
|
|
309
313
|
dispose() {
|
|
310
|
-
this.stop(), this.audioContext && (this.audioContext.close(), this.audioContext = null), this.audioChunks = [], this.scheduledChunks = 0, this.sessionStartTime = 0, this.pausedTimeOffset = 0, this.pausedAt = 0, this.pausedAudioContextTime = 0, this.scheduledTime = 0, this.onEndedCallback = void 0, this.log("StreamingAudioPlayer disposed");
|
|
314
|
+
this.stop(), this.audioContext && (this.audioContext.close(), this.audioContext = null, this.gainNode = null), this.audioChunks = [], this.scheduledChunks = 0, this.sessionStartTime = 0, this.pausedTimeOffset = 0, this.pausedAt = 0, this.pausedAudioContextTime = 0, this.scheduledTime = 0, this.onEndedCallback = void 0, this.log("StreamingAudioPlayer disposed");
|
|
311
315
|
}
|
|
312
316
|
/**
|
|
313
317
|
* Flush buffered audio
|
|
@@ -321,14 +325,29 @@ class y {
|
|
|
321
325
|
}
|
|
322
326
|
this.scheduledChunks < this.audioChunks.length && this.audioChunks.splice(this.scheduledChunks), this.log("Flushed (soft)", { remainingScheduled: this.scheduledChunks });
|
|
323
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* 设置音量 (0.0 - 1.0)
|
|
330
|
+
* 注意:这仅控制数字人音频播放器的音量,不影响系统音量
|
|
331
|
+
* @param volume 音量值,范围 0.0 到 1.0(0.0 为静音,1.0 为最大音量)
|
|
332
|
+
*/
|
|
333
|
+
setVolume(t) {
|
|
334
|
+
(t < 0 || t > 1) && (n.warn(`[StreamingAudioPlayer] Volume out of range: ${t}, clamping to [0, 1]`), t = Math.max(0, Math.min(1, t))), this.volume = t, this.gainNode && (this.gainNode.gain.value = t);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* 获取当前音量
|
|
338
|
+
* @returns 当前音量值 (0.0 - 1.0)
|
|
339
|
+
*/
|
|
340
|
+
getVolume() {
|
|
341
|
+
return this.volume;
|
|
342
|
+
}
|
|
324
343
|
/**
|
|
325
344
|
* Debug logging
|
|
326
345
|
*/
|
|
327
346
|
log(t, e) {
|
|
328
|
-
this.debug &&
|
|
347
|
+
this.debug && n.log(`[StreamingAudioPlayer] ${t}`, e || "");
|
|
329
348
|
}
|
|
330
349
|
}
|
|
331
350
|
export {
|
|
332
351
|
y as StreamingAudioPlayer
|
|
333
352
|
};
|
|
334
|
-
//# sourceMappingURL=StreamingAudioPlayer-
|
|
353
|
+
//# sourceMappingURL=StreamingAudioPlayer-DjPD7f6u.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamingAudioPlayer-DjPD7f6u.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 // Volume control\n private gainNode: GainNode | null = null\n private volume: number = 1.0 // Default volume 1.0 (0.0 - 1.0)\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 // Create GainNode for volume control\n this.gainNode = this.audioContext.createGain()\n this.gainNode.gain.value = this.volume\n this.gainNode.connect(this.audioContext.destination)\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 // Connect through gainNode for volume control\n source.connect(this.gainNode!)\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 this.gainNode = 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 * 设置音量 (0.0 - 1.0)\n * 注意:这仅控制数字人音频播放器的音量,不影响系统音量\n * @param volume 音量值,范围 0.0 到 1.0(0.0 为静音,1.0 为最大音量)\n */\n setVolume(volume: number): void {\n if (volume < 0 || volume > 1) {\n logger.warn(`[StreamingAudioPlayer] Volume out of range: ${volume}, clamping to [0, 1]`)\n volume = Math.max(0, Math.min(1, volume))\n }\n\n this.volume = volume\n if (this.gainNode) {\n this.gainNode.gain.value = volume\n }\n }\n\n /**\n * 获取当前音量\n * @returns 当前音量值 (0.0 - 1.0)\n */\n getVolume(): number {\n return this.volume\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","volume","data"],"mappings":"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"}
|
|
@@ -60,6 +60,17 @@ export declare class AnimationPlayer {
|
|
|
60
60
|
* 继续播放
|
|
61
61
|
*/
|
|
62
62
|
resume(): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* 设置音量 (0.0 - 1.0)
|
|
65
|
+
* 注意:这仅控制数字人音频播放器的音量,不影响系统音量
|
|
66
|
+
* @param volume 音量值,范围 0.0 到 1.0(0.0 为静音,1.0 为最大音量)
|
|
67
|
+
*/
|
|
68
|
+
setVolume(volume: number): void;
|
|
69
|
+
/**
|
|
70
|
+
* 获取当前音量
|
|
71
|
+
* @returns 当前音量值 (0.0 - 1.0)
|
|
72
|
+
*/
|
|
73
|
+
getVolume(): number;
|
|
63
74
|
dispose(): void;
|
|
64
75
|
}
|
|
65
76
|
//# sourceMappingURL=AnimationPlayer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnimationPlayer.d.ts","sourceRoot":"","sources":["../../audio/AnimationPlayer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAMlE,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,eAAe,CAAoC;IAC3D,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,eAAe,CAAC,CAAY;IACpC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAQ;IACpC,OAAO,CAAC,YAAY,CAAQ;IAE5B;;;OAGG;WACU,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BhD;;OAEG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBvE;;;OAGG;IACG,mBAAmB,CAAC,eAAe,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAYrG;;OAEG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;OAEG;IACH,kBAAkB,IAAI,oBAAoB,GAAG,IAAI;IAIjD;;;OAGG;IACG,kCAAkC,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BzD;;;OAGG;IACG,sBAAsB,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBjE,OAAO,CAAC,mBAAmB;IAerB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B,IAAI,IAAI,IAAI;IAcZ,SAAS,IAAI,OAAO;IAOpB,oBAAoB,IAAI,MAAM;IAK9B;;OAEG;IACH,cAAc,IAAI,MAAM;IAOxB;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,GAAE,OAAe,GAAG,IAAI;IAQ/D;;OAEG;IACH,KAAK,IAAI,IAAI;IAUb;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAU7B,OAAO,IAAI,IAAI;CAehB"}
|
|
1
|
+
{"version":3,"file":"AnimationPlayer.d.ts","sourceRoot":"","sources":["../../audio/AnimationPlayer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAMlE,qBAAa,eAAe;IAC1B,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,eAAe,CAAoC;IAC3D,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,GAAG,CAA2B;IACtC,OAAO,CAAC,eAAe,CAAC,CAAY;IACpC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAQ;IACpC,OAAO,CAAC,YAAY,CAAQ;IAE5B;;;OAGG;WACU,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BhD;;OAEG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBvE;;;OAGG;IACG,mBAAmB,CAAC,eAAe,EAAE,oBAAoB,EAAE,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAYrG;;OAEG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;OAEG;IACH,kBAAkB,IAAI,oBAAoB,GAAG,IAAI;IAIjD;;;OAGG;IACG,kCAAkC,IAAI,OAAO,CAAC,IAAI,CAAC;IA2BzD;;;OAGG;IACG,sBAAsB,CAAC,OAAO,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBjE,OAAO,CAAC,mBAAmB;IAerB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B,IAAI,IAAI,IAAI;IAcZ,SAAS,IAAI,OAAO;IAOpB,oBAAoB,IAAI,MAAM;IAK9B;;OAEG;IACH,cAAc,IAAI,MAAM;IAOxB;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,GAAE,OAAe,GAAG,IAAI;IAQ/D;;OAEG;IACH,KAAK,IAAI,IAAI;IAUb;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAU7B;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAU/B;;;OAGG;IACH,SAAS,IAAI,MAAM;IAQnB,OAAO,IAAI,IAAI;CAehB"}
|
|
@@ -25,6 +25,8 @@ export declare class StreamingAudioPlayer {
|
|
|
25
25
|
private audioChunks;
|
|
26
26
|
private scheduledChunks;
|
|
27
27
|
private activeSources;
|
|
28
|
+
private gainNode;
|
|
29
|
+
private volume;
|
|
28
30
|
private onEndedCallback?;
|
|
29
31
|
constructor(options?: StreamingAudioPlayerOptions);
|
|
30
32
|
/**
|
|
@@ -116,6 +118,17 @@ export declare class StreamingAudioPlayer {
|
|
|
116
118
|
flush(options?: {
|
|
117
119
|
hard?: boolean;
|
|
118
120
|
}): void;
|
|
121
|
+
/**
|
|
122
|
+
* 设置音量 (0.0 - 1.0)
|
|
123
|
+
* 注意:这仅控制数字人音频播放器的音量,不影响系统音量
|
|
124
|
+
* @param volume 音量值,范围 0.0 到 1.0(0.0 为静音,1.0 为最大音量)
|
|
125
|
+
*/
|
|
126
|
+
setVolume(volume: number): void;
|
|
127
|
+
/**
|
|
128
|
+
* 获取当前音量
|
|
129
|
+
* @returns 当前音量值 (0.0 - 1.0)
|
|
130
|
+
*/
|
|
131
|
+
getVolume(): number;
|
|
119
132
|
/**
|
|
120
133
|
* Debug logging
|
|
121
134
|
*/
|
|
@@ -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,CAAmC;IAGxD,OAAO,CAAC,eAAe,CAAC,CAAY;gBAExB,OAAO,CAAC,EAAE,2BAA2B;IAOjD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
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,QAAQ,CAAwB;IACxC,OAAO,CAAC,MAAM,CAAc;IAG5B,OAAO,CAAC,eAAe,CAAC,CAAY;gBAExB,OAAO,CAAC,EAAE,2BAA2B;IAOjD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsCjC;;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;IAoFzB;;;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;IAuBf;;;;OAIG;IACH,KAAK,CAAC,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAoBzC;;;;OAIG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAY/B;;;OAGG;IACH,SAAS,IAAI,MAAM;IAInB;;OAEG;IACH,OAAO,CAAC,GAAG;CAKZ"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Flame } from '../generated/driveningress/v1/driveningress';
|
|
2
2
|
import { Avatar } from './Avatar';
|
|
3
3
|
import { AnimationPlayer } from '../audio/AnimationPlayer';
|
|
4
|
-
import { AvatarState, ConnectionState,
|
|
4
|
+
import { AvatarState, ConnectionState, DrivingServiceMode, ConversationState } from '../types';
|
|
5
5
|
export declare class AvatarController {
|
|
6
6
|
private networkLayer?;
|
|
7
|
-
readonly playbackMode
|
|
7
|
+
private readonly playbackMode;
|
|
8
8
|
protected avatar: Avatar;
|
|
9
9
|
protected animationPlayer: AnimationPlayer | null;
|
|
10
10
|
protected currentKeyframes: Flame[];
|
|
@@ -18,11 +18,10 @@ export declare class AvatarController {
|
|
|
18
18
|
private currentConversationId;
|
|
19
19
|
private reqEnd;
|
|
20
20
|
onConnectionState: ((state: ConnectionState) => void) | null;
|
|
21
|
-
|
|
21
|
+
onConversationState: ((state: ConversationState) => void) | null;
|
|
22
22
|
onError: ((error: Error) => void) | null;
|
|
23
23
|
private eventListeners;
|
|
24
24
|
private renderCallback?;
|
|
25
|
-
private transitionCompleteCallback?;
|
|
26
25
|
private characterHandle;
|
|
27
26
|
private playbackLoopId;
|
|
28
27
|
private lastRenderedFrameIndex;
|
|
@@ -32,7 +31,7 @@ export declare class AvatarController {
|
|
|
32
31
|
private readonly MAX_PENDING_AUDIO_CHUNKS;
|
|
33
32
|
private isAudioOnlyMode;
|
|
34
33
|
constructor(avatar: Avatar, options?: {
|
|
35
|
-
playbackMode?:
|
|
34
|
+
playbackMode?: DrivingServiceMode;
|
|
36
35
|
});
|
|
37
36
|
/**
|
|
38
37
|
* Get current conversation ID
|
|
@@ -41,21 +40,21 @@ export declare class AvatarController {
|
|
|
41
40
|
*/
|
|
42
41
|
getCurrentConversationId(): string | null;
|
|
43
42
|
/**
|
|
44
|
-
* Start service (
|
|
43
|
+
* Start service (SDK mode only)
|
|
45
44
|
*/
|
|
46
45
|
start(): Promise<void>;
|
|
47
46
|
/**
|
|
48
|
-
* Send audio to server (
|
|
47
|
+
* Send audio to server (SDK mode only)
|
|
49
48
|
* Also cache to data layer for playback
|
|
50
49
|
* @returns conversationId - Conversation ID for this audio session
|
|
51
50
|
*/
|
|
52
51
|
send(audioData: ArrayBuffer, end?: boolean): string | null;
|
|
53
52
|
/**
|
|
54
|
-
* Close service (
|
|
53
|
+
* Close service (SDK mode only)
|
|
55
54
|
*/
|
|
56
55
|
close(): void;
|
|
57
56
|
/**
|
|
58
|
-
* Playback existing audio and animation data (
|
|
57
|
+
* Playback existing audio and animation data (host mode)
|
|
59
58
|
* Starts a new conversation by generating a new conversation ID and interrupting any existing conversation
|
|
60
59
|
* @param initialAudioChunks - Existing audio chunks to playback
|
|
61
60
|
* @param initialKeyframes - Existing animation keyframes to playback
|
|
@@ -66,13 +65,13 @@ export declare class AvatarController {
|
|
|
66
65
|
isLast: boolean;
|
|
67
66
|
}>, initialKeyframes?: Flame[]): Promise<string>;
|
|
68
67
|
/**
|
|
69
|
-
* Send audio data (
|
|
68
|
+
* Send audio data (host mode)
|
|
70
69
|
* Stream additional audio data after playback()
|
|
71
70
|
* @returns conversationId - Conversation ID for this audio session
|
|
72
71
|
*/
|
|
73
72
|
yieldAudioData(data: Uint8Array, isLast?: boolean): string | null;
|
|
74
73
|
/**
|
|
75
|
-
* Send animation keyframes (
|
|
74
|
+
* Send animation keyframes (host mode or SDK mode)
|
|
76
75
|
* Stream additional animation data after playback()
|
|
77
76
|
* @param keyframes - Animation keyframes to send
|
|
78
77
|
* @param conversationId - Conversation ID (required). If conversationId doesn't match current conversationId, keyframes will be discarded.
|
|
@@ -114,10 +113,21 @@ export declare class AvatarController {
|
|
|
114
113
|
*/
|
|
115
114
|
private resetConversationIdState;
|
|
116
115
|
/**
|
|
117
|
-
* Get effective conversation ID (handles both
|
|
116
|
+
* Get effective conversation ID (handles both SDK and host modes)
|
|
118
117
|
* @private
|
|
119
118
|
*/
|
|
120
119
|
private getEffectiveConversationId;
|
|
120
|
+
/**
|
|
121
|
+
* 设置音频播放音量
|
|
122
|
+
* 注意:这仅控制数字人音频播放器的音量,不影响系统音量
|
|
123
|
+
* @param volume 音量值,范围 0.0 到 1.0(0.0 为静音,1.0 为最大音量)
|
|
124
|
+
*/
|
|
125
|
+
setVolume(volume: number): void;
|
|
126
|
+
/**
|
|
127
|
+
* 获取当前音频播放音量
|
|
128
|
+
* @returns 当前音量值 (0.0 - 1.0)
|
|
129
|
+
*/
|
|
130
|
+
getVolume(): number;
|
|
121
131
|
/**
|
|
122
132
|
* Start streaming playback (internal implementation)
|
|
123
133
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AvatarController.d.ts","sourceRoot":"","sources":["../../core/AvatarController.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6CAA6C,CAAA;AACxE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAA;AAItC,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,eAAe,EAAiB,kBAAkB,EAAE,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"AvatarController.d.ts","sourceRoot":"","sources":["../../core/AvatarController.ts"],"names":[],"mappings":"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"}
|