@spatialwalk/avatarkit 1.0.0-beta.13 → 1.0.0-beta.14

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 CHANGED
@@ -5,6 +5,18 @@ 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.14] - 2025-11-18
9
+
10
+ ### ✨ New Features
11
+ - Added pause and resume functionality for avatar playback
12
+ - New `pause()` method to pause audio and animation playback
13
+ - New `resume()` method to resume playback from paused state
14
+ - Added `paused` state to `AvatarState` enum
15
+ - State changes are communicated via `onAvatarState` callback
16
+ - Works in both network mode and external data mode
17
+
18
+ ---
19
+
8
20
  ## [1.0.0-beta.13] - 2025-11-17
9
21
 
10
22
  ### 🔒 API Improvements
package/README.md CHANGED
@@ -362,6 +362,12 @@ avatarView.avatarController.sendKeyframes(
362
362
  #### Common Methods (Both Modes)
363
363
 
364
364
  ```typescript
365
+ // Pause playback (can be resumed later)
366
+ avatarView.avatarController.pause()
367
+
368
+ // Resume playback (from paused state)
369
+ await avatarView.avatarController.resume()
370
+
365
371
  // Interrupt current playback (stops and clears data)
366
372
  avatarView.avatarController.interrupt()
367
373
 
@@ -377,7 +383,7 @@ avatarView.avatarController.onError = (error: Error) => {}
377
383
  **Important Notes:**
378
384
  - `start()` and `close()` are only available in network mode
379
385
  - `play()`, `sendAudioChunk()`, and `sendKeyframes()` are only available in external data mode
380
- - `interrupt()` and `clear()` are available in both modes
386
+ - `pause()`, `resume()`, `interrupt()`, and `clear()` are available in both modes
381
387
  - The playback mode is determined when creating `AvatarView` and cannot be changed
382
388
 
383
389
  ## 🔧 Configuration
@@ -457,7 +463,8 @@ enum ConnectionState {
457
463
  enum AvatarState {
458
464
  idle = 'idle', // Idle state, showing breathing animation
459
465
  active = 'active', // Active, waiting for playable content
460
- playing = 'playing' // Playing
466
+ playing = 'playing', // Playing
467
+ paused = 'paused' // Paused (can be resumed)
461
468
  }
462
469
  ```
463
470
 
@@ -620,5 +627,5 @@ Issues and Pull Requests are welcome!
620
627
 
621
628
  For questions, please contact:
622
629
  - Email: support@spavatar.com
623
- - Documentation: https://docs.spavatar.com
630
+ - Documentation: https://docs.spatialreal.ai
624
631
  - GitHub: https://github.com/spavatar/sdk
@@ -1,8 +1,8 @@
1
- var g = Object.defineProperty;
2
- var f = (h, t, e) => t in h ? g(h, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : h[t] = e;
3
- var i = (h, t, e) => f(h, typeof t != "symbol" ? t + "" : t, e);
4
- import { A as C, e as m, a as c, l as r } from "./index-Cy9jPcQt.js";
5
- class A {
1
+ var C = Object.defineProperty;
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 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-CXkdiMjt.js";
5
+ class y {
6
6
  constructor(t) {
7
7
  // AudioContext is managed internally
8
8
  i(this, "audioContext", null);
@@ -17,6 +17,8 @@ class A {
17
17
  // Accumulated paused time
18
18
  i(this, "pausedAt", 0);
19
19
  // Time when paused
20
+ i(this, "pausedAudioContextTime", 0);
21
+ // audioContext.currentTime when paused (for resume calculation)
20
22
  i(this, "scheduledTime", 0);
21
23
  // Next chunk schedule time in AudioContext time
22
24
  // Playback state
@@ -31,7 +33,7 @@ class A {
31
33
  i(this, "activeSources", []);
32
34
  // Event callbacks
33
35
  i(this, "onEndedCallback");
34
- this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, this.sampleRate = (t == null ? void 0 : t.sampleRate) ?? C.audio.sampleRate, this.channelCount = (t == null ? void 0 : t.channelCount) ?? 1, this.debug = (t == null ? void 0 : t.debug) ?? !1;
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;
35
37
  }
36
38
  /**
37
39
  * Initialize audio context (create and ensure it's ready)
@@ -47,11 +49,11 @@ class A {
47
49
  state: this.audioContext.state
48
50
  });
49
51
  } catch (t) {
50
- const e = m(t);
52
+ const e = f(t);
51
53
  throw c.logEvent("activeAudioSessionFailed", "warning", {
52
54
  sessionId: this.sessionId,
53
55
  reason: e
54
- }), r.error("Failed to initialize AudioContext:", e), t instanceof Error ? t : new Error(e);
56
+ }), u.error("Failed to initialize AudioContext:", e), t instanceof Error ? t : new Error(e);
55
57
  }
56
58
  }
57
59
  /**
@@ -59,7 +61,7 @@ class A {
59
61
  */
60
62
  addChunk(t, e = !1) {
61
63
  if (!this.audioContext) {
62
- r.error("AudioContext not initialized");
64
+ u.error("AudioContext not initialized");
63
65
  return;
64
66
  }
65
67
  this.audioChunks.push({ data: t, isLast: e }), this.log(`Added chunk ${this.audioChunks.length}`, {
@@ -68,13 +70,13 @@ class A {
68
70
  isLast: e,
69
71
  isPlaying: this.isPlaying,
70
72
  scheduledChunks: this.scheduledChunks
71
- }), !this.isPlaying && this.autoStartEnabled && this.audioChunks.length > 0 ? (this.log("[StreamingAudioPlayer] Auto-starting playback from addChunk"), this.startPlayback()) : this.isPlaying ? (this.log("[StreamingAudioPlayer] Already playing, scheduling next chunk"), this.scheduleNextChunk()) : this.log("[StreamingAudioPlayer] Not playing and no chunks, waiting for more chunks");
73
+ }), !this.isPlaying && this.autoStartEnabled && this.audioChunks.length > 0 ? (this.log("[StreamingAudioPlayer] Auto-starting playback from addChunk"), this.startPlayback()) : this.isPlaying && !this.isPaused ? (this.log("[StreamingAudioPlayer] Already playing, scheduling next chunk"), this.scheduleNextChunk()) : this.log("[StreamingAudioPlayer] Not playing and no chunks, waiting for more chunks");
72
74
  }
73
75
  /**
74
76
  * Start new session (stop current and start fresh)
75
77
  */
76
78
  async startNewSession(t) {
77
- this.stop(), this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, this.audioChunks = [], this.scheduledChunks = 0, this.pausedTimeOffset = 0, this.pausedAt = 0, this.log("Starting new session", {
79
+ this.stop(), this.sessionId = `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, this.audioChunks = [], this.scheduledChunks = 0, this.pausedTimeOffset = 0, this.pausedAt = 0, this.pausedAudioContextTime = 0, this.log("Starting new session", {
78
80
  chunks: t.length
79
81
  });
80
82
  for (const e of t)
@@ -114,8 +116,8 @@ class A {
114
116
  this.log("[StreamingAudioPlayer] Cannot schedule chunk: AudioContext not initialized");
115
117
  return;
116
118
  }
117
- if (!this.isPlaying) {
118
- this.log("[StreamingAudioPlayer] Cannot schedule chunk: Not playing");
119
+ if (!this.isPlaying || this.isPaused) {
120
+ this.log("[StreamingAudioPlayer] Cannot schedule chunk: Not playing or paused");
119
121
  return;
120
122
  }
121
123
  const t = this.scheduledChunks;
@@ -128,9 +130,9 @@ class A {
128
130
  this.scheduledChunks++;
129
131
  return;
130
132
  }
131
- const l = e.data, o = e.isLast, a = this.pcmToAudioBuffer(l);
133
+ const l = e.data, d = e.isLast, a = this.pcmToAudioBuffer(l);
132
134
  if (!a) {
133
- r.error("Failed to create AudioBuffer from PCM data"), c.logEvent("character_player", "error", {
135
+ u.error("Failed to create AudioBuffer from PCM data"), c.logEvent("character_player", "error", {
134
136
  sessionId: this.sessionId,
135
137
  event: "audio_buffer_creation_failed"
136
138
  });
@@ -139,17 +141,17 @@ class A {
139
141
  try {
140
142
  const s = this.audioContext.createBufferSource();
141
143
  s.buffer = a, s.connect(this.audioContext.destination), s.start(this.scheduledTime), this.activeSources.push(s), s.onended = () => {
142
- const u = this.activeSources.indexOf(s);
143
- u >= 0 && this.activeSources.splice(u, 1), o && this.activeSources.length === 0 && (this.log("Last audio chunk ended, marking playback as ended"), this.markEnded());
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());
144
146
  }, this.scheduledTime += a.duration, this.scheduledChunks++, this.log(`[StreamingAudioPlayer] Scheduled chunk ${t + 1}/${this.audioChunks.length}`, {
145
147
  startTime: this.scheduledTime - a.duration,
146
148
  duration: a.duration,
147
149
  nextScheduleTime: this.scheduledTime,
148
- isLast: o,
150
+ isLast: d,
149
151
  activeSources: this.activeSources.length
150
152
  });
151
153
  } catch (s) {
152
- r.errorWithError("Failed to schedule audio chunk:", s), c.logEvent("character_player", "error", {
154
+ u.errorWithError("Failed to schedule audio chunk:", s), c.logEvent("character_player", "error", {
153
155
  sessionId: this.sessionId,
154
156
  event: "schedule_chunk_failed",
155
157
  reason: s instanceof Error ? s.message : String(s)
@@ -164,25 +166,25 @@ class A {
164
166
  if (!this.audioContext)
165
167
  return null;
166
168
  if (t.length === 0) {
167
- const u = Math.floor(this.sampleRate * 0.01), n = this.audioContext.createBuffer(
169
+ const o = Math.floor(this.sampleRate * 0.01), n = this.audioContext.createBuffer(
168
170
  this.channelCount,
169
- u,
171
+ o,
170
172
  this.sampleRate
171
173
  );
172
- for (let d = 0; d < this.channelCount; d++)
173
- n.getChannelData(d).fill(0);
174
+ for (let r = 0; r < this.channelCount; r++)
175
+ n.getChannelData(r).fill(0);
174
176
  return n;
175
177
  }
176
- const e = new Uint8Array(t), l = new Int16Array(e.buffer, 0, e.length / 2), o = l.length / this.channelCount, a = this.audioContext.createBuffer(
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
179
  this.channelCount,
178
- o,
180
+ d,
179
181
  this.sampleRate
180
182
  );
181
183
  for (let s = 0; s < this.channelCount; s++) {
182
- const u = a.getChannelData(s);
183
- for (let n = 0; n < o; n++) {
184
- const d = n * this.channelCount + s;
185
- u[n] = l[d] / 32768;
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;
186
188
  }
187
189
  }
188
190
  return a;
@@ -202,20 +204,33 @@ class A {
202
204
  * Pause playback
203
205
  */
204
206
  pause() {
205
- !this.isPlaying || this.isPaused || (this.isPaused = !0, this.pausedAt = this.getCurrentTime(), this.log("Playback paused", {
206
- pausedAt: this.pausedAt
207
+ !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) => {
208
+ u.errorWithError("Failed to suspend AudioContext:", t), this.isPaused = !1;
209
+ }), this.log("Playback paused", {
210
+ pausedAt: this.pausedAt,
211
+ pausedAudioContextTime: this.pausedAudioContextTime,
212
+ audioContextState: this.audioContext.state
207
213
  }));
208
214
  }
209
215
  /**
210
216
  * Resume playback
211
217
  */
212
218
  async resume() {
213
- if (!this.isPaused || !this.audioContext)
219
+ if (!this.isPaused || !this.audioContext || !this.isPlaying)
214
220
  return;
215
- const t = this.audioContext.currentTime - (this.sessionStartTime + this.pausedAt);
216
- this.pausedTimeOffset += t, this.isPaused = !1, this.log("Playback resumed", {
217
- pauseDuration: t,
218
- totalPausedOffset: this.pausedTimeOffset
221
+ if (this.audioContext.state === "suspended")
222
+ try {
223
+ await this.audioContext.resume();
224
+ } catch (e) {
225
+ throw u.errorWithError("Failed to resume AudioContext:", e), e;
226
+ }
227
+ const t = this.audioContext.currentTime;
228
+ this.sessionStartTime = this.pausedAudioContextTime - this.pausedAt - this.pausedTimeOffset, this.isPaused = !1, this.scheduledChunks < this.audioChunks.length && this.scheduleAllChunks(), this.log("Playback resumed", {
229
+ pausedAt: this.pausedAt,
230
+ pausedAudioContextTime: this.pausedAudioContextTime,
231
+ currentAudioContextTime: t,
232
+ adjustedSessionStartTime: this.sessionStartTime,
233
+ audioContextState: this.audioContext.state
219
234
  });
220
235
  }
221
236
  /**
@@ -223,7 +238,8 @@ class A {
223
238
  */
224
239
  stop() {
225
240
  if (this.audioContext) {
226
- this.isPlaying = !1, this.isPaused = !1, this.sessionStartTime = 0, this.scheduledTime = 0;
241
+ this.isPaused && this.audioContext.state === "suspended" && (this.audioContext.resume().catch(() => {
242
+ }), this.isPaused = !1), this.isPlaying = !1, this.isPaused = !1, this.sessionStartTime = 0, this.scheduledTime = 0;
227
243
  for (const t of this.activeSources) {
228
244
  t.onended = null;
229
245
  try {
@@ -292,7 +308,7 @@ class A {
292
308
  * Dispose and cleanup
293
309
  */
294
310
  dispose() {
295
- 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.scheduledTime = 0, this.onEndedCallback = void 0, this.log("StreamingAudioPlayer disposed");
311
+ 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");
296
312
  }
297
313
  /**
298
314
  * Flush buffered audio
@@ -310,10 +326,10 @@ class A {
310
326
  * Debug logging
311
327
  */
312
328
  log(t, e) {
313
- this.debug && r.log(`[StreamingAudioPlayer] ${t}`, e || "");
329
+ this.debug && u.log(`[StreamingAudioPlayer] ${t}`, e || "");
314
330
  }
315
331
  }
316
332
  export {
317
- A as StreamingAudioPlayer
333
+ y as StreamingAudioPlayer
318
334
  };
319
- //# sourceMappingURL=StreamingAudioPlayer-CsVJinsO.js.map
335
+ //# sourceMappingURL=StreamingAudioPlayer-zd5a2zg4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StreamingAudioPlayer-zd5a2zg4.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: 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.length,\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.push(source)\n source.onended = () => {\n // Remove from active list when it ends\n const idx = this.activeSources.indexOf(source)\n if (idx >= 0)\n this.activeSources.splice(idx, 1)\n\n // Check if this was the last chunk and all sources have ended\n if (isLast && this.activeSources.length === 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.length,\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 = []\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","idx","err","numSamples","channel","alignedData","int16Array","channelData","i","sampleIndex","elapsed","currentAudioTime","enabled","_a","callback","totalSamples","total","played","data"],"mappings":"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"}
@@ -52,6 +52,14 @@ export declare class AnimationPlayer {
52
52
  * 添加音频块(仅用于流式播放)
53
53
  */
54
54
  addAudioChunk(audio: Uint8Array, isLast?: boolean): void;
55
+ /**
56
+ * 暂停播放
57
+ */
58
+ pause(): void;
59
+ /**
60
+ * 继续播放
61
+ */
62
+ resume(): Promise<void>;
55
63
  dispose(): void;
56
64
  }
57
65
  //# 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,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,OAAO,IAAI,IAAI;CAehB"}
@@ -17,6 +17,7 @@ export declare class StreamingAudioPlayer {
17
17
  private sessionStartTime;
18
18
  private pausedTimeOffset;
19
19
  private pausedAt;
20
+ private pausedAudioContextTime;
20
21
  private scheduledTime;
21
22
  private isPlaying;
22
23
  private isPaused;
@@ -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,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;IAuB/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;IAeb;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB7B;;OAEG;IACH,IAAI,IAAI,IAAI;IAkCZ;;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;IAqBf;;;;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,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"}
@@ -27,74 +27,85 @@ export declare class AvatarController {
27
27
  playbackMode?: AvatarPlaybackMode;
28
28
  });
29
29
  /**
30
- * 启动服务(仅网络模式)
30
+ * Start service (network mode only)
31
31
  */
32
32
  start(): Promise<void>;
33
33
  /**
34
- * 发送音频到服务器(仅网络模式)
35
- * 同时缓存到数据层用于播放
34
+ * Send audio to server (network mode only)
35
+ * Also cache to data layer for playback
36
36
  */
37
37
  send(audioData: ArrayBuffer, end?: boolean): void;
38
38
  /**
39
- * 关闭服务(仅网络模式)
39
+ * Close service (network mode only)
40
40
  */
41
41
  close(): void;
42
42
  /**
43
- * 开始播放(外部数据模式)
44
- * 必须先调用此方法,传入初始数据开始播放
43
+ * Start playback (external data mode)
44
+ * Must call this method first with initial data to start playback
45
45
  */
46
46
  play(initialAudioChunks?: Array<{
47
47
  data: Uint8Array;
48
48
  isLast: boolean;
49
49
  }>, initialKeyframes?: Flame[]): Promise<void>;
50
50
  /**
51
- * 发送音频数据(外部数据模式)
52
- * play() 之后流式添加剩余音频数据
51
+ * Send audio data (external data mode)
52
+ * Stream additional audio data after play()
53
53
  */
54
54
  sendAudioChunk(data: Uint8Array, isLast?: boolean): void;
55
55
  /**
56
- * 发送动画关键帧(外部数据模式或网络模式)
57
- * play() 之后流式添加剩余动画数据
56
+ * Send animation keyframes (external data mode or network mode)
57
+ * Stream additional animation data after play()
58
58
  */
59
59
  sendKeyframes(keyframes: Flame[]): void;
60
60
  /**
61
- * 打断当前播放
61
+ * Pause playback (can be resumed later)
62
+ * Pause audio playback and stop render loop, but preserve all state (keyframes, audio buffers, etc.)
63
+ */
64
+ pause(): void;
65
+ /**
66
+ * Resume playback (from paused state)
67
+ * Resume audio playback and restart render loop
68
+ * Animation will continue from paused frame (because animation time base comes from audio, will auto-sync)
69
+ */
70
+ resume(): Promise<void>;
71
+ /**
72
+ * Interrupt current playback
62
73
  */
63
74
  interrupt(): void;
64
75
  /**
65
- * 清理所有数据和资源
76
+ * Clear all data and resources
66
77
  */
67
78
  clear(): void;
68
79
  /**
69
- * 开始流式播放(内部实现)
80
+ * Start streaming playback (internal implementation)
70
81
  */
71
82
  private startStreamingPlaybackInternal;
72
83
  /**
73
- * 播放循环:根据音频时间计算动画帧,通知渲染层渲染
84
+ * Playback loop: Calculate animation frame based on audio time, notify render layer to render
74
85
  */
75
86
  private startPlaybackLoop;
76
87
  /**
77
- * 停止播放循环
88
+ * Stop playback loop
78
89
  */
79
90
  private stopPlaybackLoop;
80
91
  /**
81
- * 停止播放
92
+ * Stop playback
82
93
  */
83
94
  protected stopPlayback(): void;
84
95
  /**
85
- * 清理播放器
96
+ * Clean up players
86
97
  */
87
98
  protected cleanupPlayers(): void;
88
99
  /**
89
- * 添加音频块到缓冲区
100
+ * Add audio chunk to buffer
90
101
  */
91
102
  private addAudioChunkToBuffer;
92
103
  /**
93
- * 事件系统
104
+ * Event system
94
105
  */
95
106
  private registerEventListener;
96
107
  /**
97
- * 触发事件
108
+ * Emit event
98
109
  */
99
110
  protected emit(event: string, data?: any): void;
100
111
  }
@@ -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;AAK1F,qBAAa,gBAAgB;IAE3B,OAAO,CAAC,YAAY,CAAC,CAAc;IACnC,SAAgB,YAAY,EAAE,kBAAkB,CAAA;IAChD,SAAS,CAAC,MAAM,EAAE,MAAM,CAAA;IAGxB,SAAS,CAAC,eAAe,EAAE,eAAe,GAAG,IAAI,CAAO;IACxD,SAAS,CAAC,gBAAgB,EAAE,KAAK,EAAE,CAAK;IACxC,SAAS,CAAC,kBAAkB,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAK;IAC/E,SAAS,CAAC,SAAS,UAAQ;IAG3B,SAAS,CAAC,WAAW,UAAQ;IAC7B,SAAS,CAAC,YAAY,EAAE,WAAW,CAAmB;IAG/C,iBAAiB,EAAE,CAAC,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC,GAAG,IAAI,CAAO;IACnE,aAAa,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,IAAI,CAAO;IAC3D,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAO;IACtD,OAAO,CAAC,cAAc,CAA+C;IAGrE,OAAO,CAAC,cAAc,CAAC,CAAuD;IAC9E,OAAO,CAAC,0BAA0B,CAAC,CAAY;IAG/C,OAAO,CAAC,cAAc,CAAsB;IAC5C,OAAO,CAAC,sBAAsB,CAAa;gBAE/B,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,kBAAkB,CAAA;KAAE;IA8D3E;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B5B;;;OAGG;IACH,IAAI,CAAC,SAAS,EAAE,WAAW,EAAE,GAAG,GAAE,OAAe,GAAG,IAAI;IAmBxD;;OAEG;IACH,KAAK,IAAI,IAAI;IAmBb;;;OAGG;IACG,IAAI,CACR,kBAAkB,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,EACjE,gBAAgB,CAAC,EAAE,KAAK,EAAE,GACzB,OAAO,CAAC,IAAI,CAAC;IAwChB;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,GAAE,OAAe,GAAG,IAAI;IAa/D;;;OAGG;IACH,aAAa,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,IAAI;IAWvC;;OAEG;IACH,SAAS,IAAI,IAAI;IAcjB;;OAEG;IACH,KAAK,IAAI,IAAI;IA0Fb;;OAEG;YACW,8BAA8B;IAyE5C;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuEzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAOxB;;OAEG;IACH,SAAS,CAAC,YAAY,IAAI,IAAI;IAY9B;;OAEG;IACH,SAAS,CAAC,cAAc,IAAI,IAAI;IAOhC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAY7B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAO7B;;OAEG;IACH,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,GAAG,IAAI;CAMhD"}
1
+ {"version":3,"file":"AvatarController.d.ts","sourceRoot":"","sources":["../../core/AvatarController.ts"],"names":[],"mappings":"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"}