@spatialwalk/avatarkit 1.0.0-beta.6 → 1.0.0-beta.61

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 (98) hide show
  1. package/CHANGELOG.md +499 -4
  2. package/README.md +267 -289
  3. package/dist/StreamingAudioPlayer-DIcPerS7.js +525 -0
  4. package/dist/animation/AnimationWebSocketClient.d.ts +9 -24
  5. package/dist/animation/utils/eventEmitter.d.ts +0 -4
  6. package/dist/animation/utils/flameConverter.d.ts +3 -11
  7. package/dist/audio/AnimationPlayer.d.ts +4 -32
  8. package/dist/audio/StreamingAudioPlayer.d.ts +14 -75
  9. package/dist/avatar_core_wasm-i0Ocpx6q.js +2693 -0
  10. package/dist/avatar_core_wasm.wasm +0 -0
  11. package/dist/config/app-config.d.ts +1 -6
  12. package/dist/config/constants.d.ts +11 -25
  13. package/dist/config/sdk-config-loader.d.ts +4 -9
  14. package/dist/core/Avatar.d.ts +0 -14
  15. package/dist/core/AvatarController.d.ts +44 -116
  16. package/dist/core/AvatarDownloader.d.ts +0 -95
  17. package/dist/core/AvatarManager.d.ts +10 -18
  18. package/dist/core/AvatarSDK.d.ts +21 -0
  19. package/dist/core/AvatarView.d.ts +34 -110
  20. package/dist/core/NetworkLayer.d.ts +1 -59
  21. package/dist/generated/common/v1/models.d.ts +29 -0
  22. package/dist/generated/driveningress/v1/driveningress.d.ts +1 -12
  23. package/dist/generated/driveningress/v2/driveningress.d.ts +81 -3
  24. package/dist/generated/google/protobuf/struct.d.ts +5 -39
  25. package/dist/generated/google/protobuf/timestamp.d.ts +1 -103
  26. package/dist/index-jWgogoMs.js +14758 -0
  27. package/dist/index.d.ts +1 -6
  28. package/dist/index.js +17 -18
  29. package/dist/renderer/RenderSystem.d.ts +1 -79
  30. package/dist/renderer/covariance.d.ts +0 -12
  31. package/dist/renderer/renderer.d.ts +6 -2
  32. package/dist/renderer/sortSplats.d.ts +0 -11
  33. package/dist/renderer/webgl/reorderData.d.ts +0 -13
  34. package/dist/renderer/webgl/webglRenderer.d.ts +19 -42
  35. package/dist/renderer/webgpu/webgpuRenderer.d.ts +18 -31
  36. package/dist/types/character-settings.d.ts +1 -5
  37. package/dist/types/character.d.ts +3 -21
  38. package/dist/types/index.d.ts +91 -36
  39. package/dist/utils/animation-interpolation.d.ts +3 -13
  40. package/dist/utils/client-id.d.ts +1 -0
  41. package/dist/utils/conversationId.d.ts +1 -0
  42. package/dist/utils/error-utils.d.ts +1 -25
  43. package/dist/utils/id-manager.d.ts +38 -0
  44. package/dist/utils/logger.d.ts +5 -11
  45. package/dist/utils/posthog-tracker.d.ts +11 -0
  46. package/dist/utils/pwa-cache-manager.d.ts +16 -0
  47. package/dist/utils/usage-tracker.d.ts +5 -0
  48. package/dist/vanilla/vite.config.d.ts +2 -0
  49. package/dist/wasm/avatarCoreAdapter.d.ts +14 -99
  50. package/dist/wasm/avatarCoreMemory.d.ts +5 -54
  51. package/package.json +15 -13
  52. package/dist/StreamingAudioPlayer-BKTD97fl.js +0 -319
  53. package/dist/StreamingAudioPlayer-BKTD97fl.js.map +0 -1
  54. package/dist/animation/AnimationWebSocketClient.d.ts.map +0 -1
  55. package/dist/animation/utils/eventEmitter.d.ts.map +0 -1
  56. package/dist/animation/utils/flameConverter.d.ts.map +0 -1
  57. package/dist/audio/AnimationPlayer.d.ts.map +0 -1
  58. package/dist/audio/StreamingAudioPlayer.d.ts.map +0 -1
  59. package/dist/avatar_core_wasm-D4eEi7Eh.js +0 -1666
  60. package/dist/avatar_core_wasm-D4eEi7Eh.js.map +0 -1
  61. package/dist/config/app-config.d.ts.map +0 -1
  62. package/dist/config/constants.d.ts.map +0 -1
  63. package/dist/config/sdk-config-loader.d.ts.map +0 -1
  64. package/dist/core/Avatar.d.ts.map +0 -1
  65. package/dist/core/AvatarController.d.ts.map +0 -1
  66. package/dist/core/AvatarDownloader.d.ts.map +0 -1
  67. package/dist/core/AvatarKit.d.ts +0 -66
  68. package/dist/core/AvatarKit.d.ts.map +0 -1
  69. package/dist/core/AvatarManager.d.ts.map +0 -1
  70. package/dist/core/AvatarView.d.ts.map +0 -1
  71. package/dist/core/NetworkLayer.d.ts.map +0 -1
  72. package/dist/generated/driveningress/v1/driveningress.d.ts.map +0 -1
  73. package/dist/generated/driveningress/v2/driveningress.d.ts.map +0 -1
  74. package/dist/generated/google/protobuf/struct.d.ts.map +0 -1
  75. package/dist/generated/google/protobuf/timestamp.d.ts.map +0 -1
  76. package/dist/index-CX8f1bzw.js +0 -5946
  77. package/dist/index-CX8f1bzw.js.map +0 -1
  78. package/dist/index.d.ts.map +0 -1
  79. package/dist/index.js.map +0 -1
  80. package/dist/renderer/RenderSystem.d.ts.map +0 -1
  81. package/dist/renderer/covariance.d.ts.map +0 -1
  82. package/dist/renderer/renderer.d.ts.map +0 -1
  83. package/dist/renderer/sortSplats.d.ts.map +0 -1
  84. package/dist/renderer/webgl/reorderData.d.ts.map +0 -1
  85. package/dist/renderer/webgl/webglRenderer.d.ts.map +0 -1
  86. package/dist/renderer/webgpu/webgpuRenderer.d.ts.map +0 -1
  87. package/dist/types/character-settings.d.ts.map +0 -1
  88. package/dist/types/character.d.ts.map +0 -1
  89. package/dist/types/index.d.ts.map +0 -1
  90. package/dist/utils/animation-interpolation.d.ts.map +0 -1
  91. package/dist/utils/cls-tracker.d.ts +0 -17
  92. package/dist/utils/cls-tracker.d.ts.map +0 -1
  93. package/dist/utils/error-utils.d.ts.map +0 -1
  94. package/dist/utils/logger.d.ts.map +0 -1
  95. package/dist/utils/reqId.d.ts +0 -20
  96. package/dist/utils/reqId.d.ts.map +0 -1
  97. package/dist/wasm/avatarCoreAdapter.d.ts.map +0 -1
  98. package/dist/wasm/avatarCoreMemory.d.ts.map +0 -1
@@ -0,0 +1,525 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-jWgogoMs.js";
5
+ class StreamingAudioPlayer {
6
+ constructor(options) {
7
+ __publicField(this, "audioContext", null);
8
+ __publicField(this, "sampleRate");
9
+ __publicField(this, "channelCount");
10
+ __publicField(this, "debug");
11
+ __publicField(this, "sessionId");
12
+ __publicField(this, "sessionStartTime", 0);
13
+ __publicField(this, "pausedTimeOffset", 0);
14
+ __publicField(this, "pausedAt", 0);
15
+ __publicField(this, "pausedAudioContextTime", 0);
16
+ __publicField(this, "scheduledTime", 0);
17
+ __publicField(this, "isPlaying", false);
18
+ __publicField(this, "isPaused", false);
19
+ __publicField(this, "autoStartEnabled", true);
20
+ __publicField(this, "autoContinue", false);
21
+ __publicField(this, "audioChunks", []);
22
+ __publicField(this, "scheduledChunks", 0);
23
+ __publicField(this, "activeSources", /* @__PURE__ */ new Set());
24
+ __publicField(this, "lastScheduledChunkEndTime", 0);
25
+ __publicField(this, "lastGetCurrentTimeLog", 0);
26
+ __publicField(this, "scheduledChunkInfo", []);
27
+ __publicField(this, "gainNode", null);
28
+ __publicField(this, "volume", 1);
29
+ __publicField(this, "onEndedCallback");
30
+ __publicField(this, "stateChangeHandler");
31
+ __publicField(this, "isResuming", false);
32
+ this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
33
+ this.sampleRate = (options == null ? void 0 : options.sampleRate) ?? APP_CONFIG.audio.sampleRate;
34
+ this.channelCount = (options == null ? void 0 : options.channelCount) ?? 1;
35
+ this.debug = (options == null ? void 0 : options.debug) ?? false;
36
+ }
37
+ async initialize() {
38
+ if (this.audioContext) {
39
+ return;
40
+ }
41
+ try {
42
+ this.audioContext = new AudioContext({
43
+ sampleRate: this.sampleRate
44
+ });
45
+ this.gainNode = this.audioContext.createGain();
46
+ this.gainNode.gain.value = this.volume;
47
+ this.gainNode.connect(this.audioContext.destination);
48
+ if (this.audioContext.state === "suspended") {
49
+ await this.audioContext.resume();
50
+ }
51
+ this.stateChangeHandler = (event) => {
52
+ const context = event.target;
53
+ if (context.state === "suspended" && this.isPlaying && !this.isPaused) {
54
+ this.ensureAudioContextRunning().catch((err) => {
55
+ logger.errorWithError("[StreamingAudioPlayer] Failed to auto-resume AudioContext after external suspend:", err);
56
+ });
57
+ }
58
+ };
59
+ this.audioContext.addEventListener("statechange", this.stateChangeHandler);
60
+ this.log("AudioContext initialized", {
61
+ sessionId: this.sessionId,
62
+ sampleRate: this.audioContext.sampleRate,
63
+ state: this.audioContext.state
64
+ });
65
+ } catch (error) {
66
+ const message = errorToMessage(error);
67
+ logEvent("activeAudioSessionFailed", "warning", {
68
+ sessionId: this.sessionId,
69
+ reason: message
70
+ });
71
+ logger.error("Failed to initialize AudioContext:", message);
72
+ throw error instanceof Error ? error : new Error(message);
73
+ }
74
+ }
75
+ async ensureAudioContextRunning() {
76
+ if (!this.audioContext) {
77
+ return;
78
+ }
79
+ const state = this.audioContext.state;
80
+ if (state === "running") {
81
+ return;
82
+ }
83
+ if (state === "closed") {
84
+ this.log("AudioContext is closed, cannot resume", {
85
+ sessionId: this.sessionId,
86
+ state
87
+ });
88
+ return;
89
+ }
90
+ if (state === "suspended" && this.isPlaying && !this.isPaused) {
91
+ if (this.isResuming) {
92
+ this.log("AudioContext resume already in progress, skipping duplicate request", {
93
+ sessionId: this.sessionId,
94
+ state
95
+ });
96
+ return;
97
+ }
98
+ this.isResuming = true;
99
+ try {
100
+ this.log("AudioContext is suspended during playback, resuming...", {
101
+ sessionId: this.sessionId,
102
+ state,
103
+ isPlaying: this.isPlaying,
104
+ isPaused: this.isPaused
105
+ });
106
+ await this.audioContext.resume();
107
+ this.log("AudioContext resumed successfully", {
108
+ sessionId: this.sessionId,
109
+ state: this.audioContext.state
110
+ });
111
+ } catch (err) {
112
+ logger.errorWithError("[StreamingAudioPlayer] Failed to resume AudioContext:", err);
113
+ logEvent("character_player", "error", {
114
+ sessionId: this.sessionId,
115
+ event: "audio_context_resume_failed",
116
+ reason: err instanceof Error ? err.message : String(err)
117
+ });
118
+ } finally {
119
+ this.isResuming = false;
120
+ }
121
+ }
122
+ }
123
+ addChunk(pcmData, isLast = false) {
124
+ if (!this.audioContext) {
125
+ logger.error("AudioContext not initialized");
126
+ return;
127
+ }
128
+ if (this.isPlaying && !this.isPaused && this.audioContext.state === "suspended") {
129
+ this.ensureAudioContextRunning().catch((err) => {
130
+ logger.errorWithError("[StreamingAudioPlayer] Failed to ensure AudioContext running in addChunk:", err);
131
+ });
132
+ }
133
+ this.audioChunks.push({ data: pcmData, isLast });
134
+ this.log(`Added chunk ${this.audioChunks.length}`, {
135
+ size: pcmData.length,
136
+ totalChunks: this.audioChunks.length,
137
+ isLast,
138
+ isPlaying: this.isPlaying,
139
+ scheduledChunks: this.scheduledChunks
140
+ });
141
+ if (this.autoContinue && this.isPaused) {
142
+ this.log("[StreamingAudioPlayer] autoContinue=true, auto-resuming playback");
143
+ this.autoContinue = false;
144
+ this.resume().catch((err) => {
145
+ logger.errorWithError("Failed to auto-resume playback:", err);
146
+ });
147
+ }
148
+ if (!this.isPlaying && this.autoStartEnabled && this.audioChunks.length > 0) {
149
+ this.log("[StreamingAudioPlayer] Auto-starting playback from addChunk");
150
+ this.startPlayback().catch((err) => {
151
+ logger.errorWithError("[StreamingAudioPlayer] Failed to start playback from addChunk:", err);
152
+ });
153
+ } else if (this.isPlaying && !this.isPaused) {
154
+ this.log("[StreamingAudioPlayer] Already playing, scheduling next chunk");
155
+ this.scheduleNextChunk();
156
+ } else {
157
+ this.log("[StreamingAudioPlayer] Not playing and no chunks, waiting for more chunks");
158
+ }
159
+ }
160
+ async startNewSession(audioChunks) {
161
+ this.stop();
162
+ this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
163
+ this.audioChunks = [];
164
+ this.scheduledChunks = 0;
165
+ this.pausedTimeOffset = 0;
166
+ this.pausedAt = 0;
167
+ this.pausedAudioContextTime = 0;
168
+ this.autoContinue = false;
169
+ this.log("Starting new session", {
170
+ chunks: audioChunks.length
171
+ });
172
+ for (const chunk of audioChunks) {
173
+ this.addChunk(chunk.data, chunk.isLast);
174
+ }
175
+ }
176
+ async startPlayback() {
177
+ if (!this.audioContext) {
178
+ this.log("[StreamingAudioPlayer] Cannot start playback: AudioContext not initialized");
179
+ return;
180
+ }
181
+ if (this.isPlaying) {
182
+ this.log("[StreamingAudioPlayer] Cannot start playback: Already playing");
183
+ return;
184
+ }
185
+ await this.ensureAudioContextRunning();
186
+ this.isPlaying = true;
187
+ this.sessionStartTime = this.audioContext.currentTime;
188
+ this.scheduledTime = this.sessionStartTime;
189
+ this.lastScheduledChunkEndTime = 0;
190
+ this.scheduledChunkInfo = [];
191
+ this.autoContinue = false;
192
+ this.log("[StreamingAudioPlayer] Starting playback", {
193
+ sessionStartTime: this.sessionStartTime,
194
+ bufferedChunks: this.audioChunks.length,
195
+ scheduledChunks: this.scheduledChunks,
196
+ activeSources: this.activeSources.size,
197
+ audioContextState: this.audioContext.state
198
+ });
199
+ this.scheduleAllChunks();
200
+ }
201
+ scheduleAllChunks() {
202
+ while (this.scheduledChunks < this.audioChunks.length) {
203
+ this.scheduleNextChunk();
204
+ }
205
+ }
206
+ scheduleNextChunk() {
207
+ if (!this.audioContext) {
208
+ this.log("[StreamingAudioPlayer] Cannot schedule chunk: AudioContext not initialized");
209
+ return;
210
+ }
211
+ if (!this.isPlaying || this.isPaused) {
212
+ this.log("[StreamingAudioPlayer] Cannot schedule chunk: Not playing or paused");
213
+ return;
214
+ }
215
+ if (this.audioContext.state === "suspended") {
216
+ this.ensureAudioContextRunning().catch((err) => {
217
+ logger.errorWithError("[StreamingAudioPlayer] Failed to ensure AudioContext running in scheduleNextChunk:", err);
218
+ });
219
+ }
220
+ const chunkIndex = this.scheduledChunks;
221
+ if (chunkIndex >= this.audioChunks.length) {
222
+ this.log(`[StreamingAudioPlayer] No more chunks to schedule (chunkIndex: ${chunkIndex}, totalChunks: ${this.audioChunks.length})`);
223
+ return;
224
+ }
225
+ const chunk = this.audioChunks[chunkIndex];
226
+ if (chunk.data.length === 0 && !chunk.isLast) {
227
+ this.scheduledChunks++;
228
+ return;
229
+ }
230
+ const pcmData = chunk.data;
231
+ const isLast = chunk.isLast;
232
+ const audioBuffer = this.pcmToAudioBuffer(pcmData);
233
+ if (!audioBuffer) {
234
+ const errorMessage = "Failed to create AudioBuffer from PCM data";
235
+ logger.error(errorMessage);
236
+ logEvent("character_player", "error", {
237
+ sessionId: this.sessionId,
238
+ event: "audio_buffer_creation_failed"
239
+ });
240
+ return;
241
+ }
242
+ try {
243
+ const source = this.audioContext.createBufferSource();
244
+ source.buffer = audioBuffer;
245
+ source.connect(this.gainNode);
246
+ const chunkStartTime = this.scheduledTime;
247
+ source.start(chunkStartTime);
248
+ const actualStartTime = Math.max(chunkStartTime, this.audioContext.currentTime);
249
+ this.scheduledChunkInfo.push({
250
+ startTime: actualStartTime,
251
+ duration: audioBuffer.duration
252
+ });
253
+ this.activeSources.add(source);
254
+ source.onended = () => {
255
+ this.activeSources.delete(source);
256
+ if (this.activeSources.size === 0) {
257
+ const lastChunk = this.audioChunks[this.scheduledChunks - 1];
258
+ if (lastChunk && !lastChunk.isLast) {
259
+ this.log("All audio chunks ended but end=false, pausing and setting autoContinue");
260
+ this.autoContinue = true;
261
+ this.pause();
262
+ } else if (isLast) {
263
+ this.log("Last audio chunk ended, marking playback as ended");
264
+ this.markEnded();
265
+ }
266
+ }
267
+ };
268
+ this.scheduledTime += audioBuffer.duration;
269
+ this.lastScheduledChunkEndTime = this.scheduledTime - this.sessionStartTime - this.pausedTimeOffset;
270
+ this.scheduledChunks++;
271
+ this.log(`[StreamingAudioPlayer] Scheduled chunk ${chunkIndex + 1}/${this.audioChunks.length}`, {
272
+ startTime: this.scheduledTime - audioBuffer.duration,
273
+ duration: audioBuffer.duration,
274
+ nextScheduleTime: this.scheduledTime,
275
+ isLast,
276
+ activeSources: this.activeSources.size
277
+ });
278
+ } catch (err) {
279
+ logger.errorWithError("Failed to schedule audio chunk:", err);
280
+ logEvent("character_player", "error", {
281
+ sessionId: this.sessionId,
282
+ event: "schedule_chunk_failed",
283
+ reason: err instanceof Error ? err.message : String(err)
284
+ });
285
+ }
286
+ }
287
+ pcmToAudioBuffer(pcmData) {
288
+ if (!this.audioContext) {
289
+ return null;
290
+ }
291
+ if (pcmData.length === 0) {
292
+ const silenceDuration = 0.01;
293
+ const numSamples2 = Math.floor(this.sampleRate * silenceDuration);
294
+ const audioBuffer2 = this.audioContext.createBuffer(
295
+ this.channelCount,
296
+ numSamples2,
297
+ this.sampleRate
298
+ );
299
+ for (let channel = 0; channel < this.channelCount; channel++) {
300
+ const channelData = audioBuffer2.getChannelData(channel);
301
+ channelData.fill(0);
302
+ }
303
+ return audioBuffer2;
304
+ }
305
+ const alignedData = new Uint8Array(pcmData);
306
+ const int16Array = new Int16Array(alignedData.buffer, 0, alignedData.length / 2);
307
+ const numSamples = int16Array.length / this.channelCount;
308
+ const audioBuffer = this.audioContext.createBuffer(
309
+ this.channelCount,
310
+ numSamples,
311
+ this.sampleRate
312
+ );
313
+ for (let channel = 0; channel < this.channelCount; channel++) {
314
+ const channelData = audioBuffer.getChannelData(channel);
315
+ for (let i = 0; i < numSamples; i++) {
316
+ const sampleIndex = i * this.channelCount + channel;
317
+ channelData[i] = int16Array[sampleIndex] / 32768;
318
+ }
319
+ }
320
+ return audioBuffer;
321
+ }
322
+ getCurrentTime() {
323
+ if (!this.audioContext || !this.isPlaying) {
324
+ return 0;
325
+ }
326
+ if (this.isPaused) {
327
+ return this.pausedAt;
328
+ }
329
+ const currentAudioTime = this.audioContext.currentTime;
330
+ if (this.activeSources.size === 0 && this.scheduledChunks > 0) {
331
+ return Math.max(0, this.lastScheduledChunkEndTime);
332
+ }
333
+ let totalPlayedDuration = 0;
334
+ for (let i = 0; i < this.scheduledChunkInfo.length; i++) {
335
+ const chunkInfo = this.scheduledChunkInfo[i];
336
+ const chunkEndTime = chunkInfo.startTime + chunkInfo.duration;
337
+ if (currentAudioTime < chunkInfo.startTime) {
338
+ break;
339
+ } else if (chunkEndTime <= currentAudioTime) {
340
+ totalPlayedDuration += chunkInfo.duration;
341
+ } else {
342
+ const playedTime = currentAudioTime - chunkInfo.startTime;
343
+ totalPlayedDuration += playedTime;
344
+ break;
345
+ }
346
+ }
347
+ return Math.max(0, totalPlayedDuration);
348
+ }
349
+ getBufferedDuration() {
350
+ if (!this.audioContext) {
351
+ return 0;
352
+ }
353
+ let totalDuration = 0;
354
+ for (const chunk of this.audioChunks) {
355
+ const chunkDuration = chunk.data.length / (this.sampleRate * this.channelCount * 2);
356
+ totalDuration += chunkDuration;
357
+ }
358
+ return totalDuration;
359
+ }
360
+ getAudioContextTime() {
361
+ var _a;
362
+ return ((_a = this.audioContext) == null ? void 0 : _a.currentTime) ?? 0;
363
+ }
364
+ pause() {
365
+ if (!this.isPlaying || this.isPaused || !this.audioContext) {
366
+ return;
367
+ }
368
+ this.pausedAt = this.getCurrentTime();
369
+ this.pausedAudioContextTime = this.audioContext.currentTime;
370
+ this.isPaused = true;
371
+ if (this.audioContext.state === "running") {
372
+ this.audioContext.suspend().catch((err) => {
373
+ logger.errorWithError("Failed to suspend AudioContext:", err);
374
+ this.isPaused = false;
375
+ });
376
+ }
377
+ this.log("Playback paused", {
378
+ pausedAt: this.pausedAt,
379
+ pausedAudioContextTime: this.pausedAudioContextTime,
380
+ audioContextState: this.audioContext.state
381
+ });
382
+ }
383
+ async resume() {
384
+ if (!this.isPaused || !this.audioContext || !this.isPlaying) {
385
+ return;
386
+ }
387
+ this.autoContinue = false;
388
+ if (this.audioContext.state === "suspended") {
389
+ try {
390
+ await this.audioContext.resume();
391
+ } catch (err) {
392
+ logger.errorWithError("Failed to resume AudioContext:", err);
393
+ throw err;
394
+ }
395
+ }
396
+ const currentAudioTime = this.audioContext.currentTime;
397
+ this.sessionStartTime = this.pausedAudioContextTime - this.pausedAt - this.pausedTimeOffset;
398
+ this.isPaused = false;
399
+ if (this.scheduledChunks < this.audioChunks.length) {
400
+ this.scheduleAllChunks();
401
+ }
402
+ this.log("Playback resumed", {
403
+ pausedAt: this.pausedAt,
404
+ pausedAudioContextTime: this.pausedAudioContextTime,
405
+ currentAudioContextTime: currentAudioTime,
406
+ adjustedSessionStartTime: this.sessionStartTime,
407
+ audioContextState: this.audioContext.state
408
+ });
409
+ }
410
+ stop() {
411
+ if (!this.audioContext) {
412
+ return;
413
+ }
414
+ if (this.isPaused && this.audioContext.state === "suspended") {
415
+ this.audioContext.resume().catch(() => {
416
+ });
417
+ this.isPaused = false;
418
+ }
419
+ this.isPlaying = false;
420
+ this.isPaused = false;
421
+ this.isResuming = false;
422
+ this.sessionStartTime = 0;
423
+ this.scheduledTime = 0;
424
+ for (const source of this.activeSources) {
425
+ source.onended = null;
426
+ try {
427
+ source.stop(0);
428
+ } catch {
429
+ }
430
+ try {
431
+ source.disconnect();
432
+ } catch {
433
+ }
434
+ }
435
+ this.activeSources.clear();
436
+ this.audioChunks = [];
437
+ this.scheduledChunks = 0;
438
+ this.autoContinue = false;
439
+ this.log("[StreamingAudioPlayer] Playback stopped, state reset");
440
+ }
441
+ setAutoStart(enabled) {
442
+ this.autoStartEnabled = enabled;
443
+ this.log(`Auto-start ${enabled ? "enabled" : "disabled"}`);
444
+ }
445
+ play() {
446
+ if (this.isPlaying) {
447
+ return;
448
+ }
449
+ this.autoStartEnabled = true;
450
+ this.startPlayback().catch((err) => {
451
+ logger.errorWithError("[StreamingAudioPlayer] Failed to start playback from play():", err);
452
+ });
453
+ }
454
+ markEnded() {
455
+ var _a;
456
+ this.log("Playback ended");
457
+ this.isPlaying = false;
458
+ (_a = this.onEndedCallback) == null ? void 0 : _a.call(this);
459
+ }
460
+ onEnded(callback) {
461
+ this.onEndedCallback = callback;
462
+ }
463
+ isPlayingNow() {
464
+ return this.isPlaying && !this.isPaused;
465
+ }
466
+ dispose() {
467
+ this.stop();
468
+ if (this.audioContext && this.stateChangeHandler) {
469
+ this.audioContext.removeEventListener("statechange", this.stateChangeHandler);
470
+ this.stateChangeHandler = void 0;
471
+ }
472
+ if (this.audioContext) {
473
+ this.audioContext.close();
474
+ this.audioContext = null;
475
+ this.gainNode = null;
476
+ }
477
+ this.audioChunks = [];
478
+ this.scheduledChunks = 0;
479
+ this.sessionStartTime = 0;
480
+ this.pausedTimeOffset = 0;
481
+ this.pausedAt = 0;
482
+ this.pausedAudioContextTime = 0;
483
+ this.scheduledTime = 0;
484
+ this.onEndedCallback = void 0;
485
+ this.log("StreamingAudioPlayer disposed");
486
+ }
487
+ flush(options) {
488
+ const hard = (options == null ? void 0 : options.hard) === true;
489
+ if (hard) {
490
+ this.stop();
491
+ this.audioChunks = [];
492
+ this.scheduledChunks = 0;
493
+ this.sessionStartTime = 0;
494
+ this.pausedAt = 0;
495
+ this.scheduledTime = 0;
496
+ this.log("Flushed (hard)");
497
+ return;
498
+ }
499
+ if (this.scheduledChunks < this.audioChunks.length) {
500
+ this.audioChunks.splice(this.scheduledChunks);
501
+ }
502
+ this.log("Flushed (soft)", { remainingScheduled: this.scheduledChunks });
503
+ }
504
+ setVolume(volume) {
505
+ if (volume < 0 || volume > 1) {
506
+ logger.warn(`[StreamingAudioPlayer] Volume out of range: ${volume}, clamping to [0, 1]`);
507
+ volume = Math.max(0, Math.min(1, volume));
508
+ }
509
+ this.volume = volume;
510
+ if (this.gainNode) {
511
+ this.gainNode.gain.value = volume;
512
+ }
513
+ }
514
+ getVolume() {
515
+ return this.volume;
516
+ }
517
+ log(message, data) {
518
+ if (this.debug) {
519
+ logger.log(`[StreamingAudioPlayer] ${message}`, data || "");
520
+ }
521
+ }
522
+ }
523
+ export {
524
+ StreamingAudioPlayer
525
+ };
@@ -2,49 +2,34 @@ import { EventEmitter } from './utils/eventEmitter';
2
2
  export interface AnimationWebSocketClientOptions {
3
3
  wsUrl: string;
4
4
  reconnectAttempts?: number;
5
- debug?: boolean;
6
5
  jwtToken?: string;
6
+ appId?: string;
7
+ clientId?: string;
7
8
  }
8
9
  export declare class AnimationWebSocketClient extends EventEmitter {
9
10
  private wsUrl;
10
11
  private reconnectAttempts;
11
- private debug;
12
12
  private jwtToken?;
13
+ private appId?;
14
+ private clientId?;
13
15
  private ws;
14
16
  private currentCharacterId;
15
17
  private currentRetryCount;
16
18
  private isConnecting;
17
19
  private isManuallyDisconnected;
18
20
  private reconnectTimer;
21
+ private sessionConfigured;
19
22
  constructor(options: AnimationWebSocketClientOptions);
20
- /**
21
- * 连接WebSocket
22
- */
23
23
  connect(characterId: string): Promise<void>;
24
- /**
25
- * 断开连接
26
- */
27
24
  disconnect(): void;
28
- /**
29
- * 发送音频数据
30
- */
31
- sendAudioData(reqId: string, audioData: ArrayBuffer, end: boolean): boolean;
32
- /**
33
- * 生成请求ID
34
- * 使用统一的 ReqID 生成规则:YYYYMMDDHHmmss_nanoid
35
- */
36
- generateReqId(): string;
37
- /**
38
- * 获取连接状态
39
- */
25
+ sendAudioData(conversationId: string, audioData: ArrayBuffer, end: boolean): boolean;
26
+ generateConversationId(): string;
40
27
  isConnected(): boolean;
41
- /**
42
- * 获取当前角色ID
43
- */
44
28
  getCurrentCharacterId(): string;
45
29
  private buildWebSocketUrl;
46
30
  private connectWebSocket;
31
+ private sanitizeUrlForLog;
32
+ private configureSession;
47
33
  private handleMessage;
48
34
  private scheduleReconnect;
49
35
  }
50
- //# sourceMappingURL=AnimationWebSocketClient.d.ts.map
@@ -1,6 +1,3 @@
1
- /**
2
- * Simple Event Emitter
3
- */
4
1
  type EventHandler = (...args: any[]) => void;
5
2
  export declare class EventEmitter {
6
3
  private events;
@@ -10,4 +7,3 @@ export declare class EventEmitter {
10
7
  removeAllListeners(event?: string): void;
11
8
  }
12
9
  export {};
13
- //# sourceMappingURL=eventEmitter.d.ts.map
@@ -10,17 +10,9 @@ export interface FlameParams {
10
10
  eyelid?: number[];
11
11
  has_eyelid?: boolean;
12
12
  }
13
- /**
14
- * Convert proto Flame to WASM FlameParams format
15
- */
13
+
16
14
  export declare function convertProtoFlameToWasmParams(protoFlame: Flame): FlameParams;
17
- /**
18
- * Convert WASM FlameParams to proto Flame format
19
- * Used for transition animation from idle to speaking
20
- */
15
+
21
16
  export declare function convertWasmParamsToProtoFlame(wasmParams: FlameParams): Flame;
22
- /**
23
- * Create a neutral proto Flame (zero pose)
24
- */
17
+
25
18
  export declare function createNeutralFlameProto(): Flame;
26
- //# sourceMappingURL=flameConverter.d.ts.map
@@ -7,51 +7,23 @@ export declare class AnimationPlayer {
7
7
  private onEndedCallback?;
8
8
  private static audioUnlocked;
9
9
  private useStreaming;
10
- /**
11
- * 解锁音频上下文(Safari 自动播放策略)
12
- * 必须在用户交互事件(如 click)中调用
13
- */
14
10
  static unlockAudioContext(): Promise<void>;
15
- /**
16
- * Initialize with HTMLAudioElement (traditional way)
17
- */
18
11
  initialize(audioUrl: string, onEnded?: () => void): Promise<void>;
19
- /**
20
- * Initialize with StreamingAudioPlayer (streaming way)
21
- * @deprecated 使用 prepareStreamingPlayer() 代替
22
- */
23
12
  initializeStreaming(streamingPlayer: StreamingAudioPlayer, onEnded?: () => void): Promise<void>;
24
- /**
25
- * 检查流式播放器是否已准备好
26
- */
27
13
  isStreamingReady(): boolean;
28
- /**
29
- * 获取流式播放器实例
30
- */
31
14
  getStreamingPlayer(): StreamingAudioPlayer | null;
32
- /**
33
- * 创建并初始化流式播放器
34
- * 在服务连接建立时调用
35
- */
36
15
  createAndInitializeStreamingPlayer(): Promise<void>;
37
- /**
38
- * 准备流式播放器(如果未创建则创建并初始化)
39
- * 停止之前的播放并更新结束回调
40
- */
41
16
  prepareStreamingPlayer(onEnded?: () => void): Promise<void>;
42
17
  private setupEventListeners;
43
18
  play(): Promise<void>;
44
19
  stop(): void;
45
20
  isPlaying(): boolean;
46
21
  getCurrentFrameIndex(): number;
47
- /**
48
- * Get current playback time
49
- */
50
22
  getCurrentTime(): number;
51
- /**
52
- * 添加音频块(仅用于流式播放)
53
- */
54
23
  addAudioChunk(audio: Uint8Array, isLast?: boolean): void;
24
+ pause(): void;
25
+ resume(): Promise<void>;
26
+ setVolume(volume: number): void;
27
+ getVolume(): number;
55
28
  dispose(): void;
56
29
  }
57
- //# sourceMappingURL=AnimationPlayer.d.ts.map