@spatialwalk/avatarkit 1.0.0-beta.10 → 1.0.0-beta.101

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 +771 -4
  2. package/README.md +676 -365
  3. package/dist/StreamingAudioPlayer-BULgPjpe.js +643 -0
  4. package/dist/avatar_core_wasm-CQbUl6zN.js +2696 -0
  5. package/dist/avatar_core_wasm-bd762669.wasm +0 -0
  6. package/dist/core/Avatar.d.ts +5 -7
  7. package/dist/core/AvatarController.d.ts +99 -60
  8. package/dist/core/AvatarManager.d.ts +32 -12
  9. package/dist/core/AvatarSDK.d.ts +58 -0
  10. package/dist/core/AvatarView.d.ts +136 -128
  11. package/dist/index-C0A1HA8M.js +18427 -0
  12. package/dist/index.d.ts +2 -4
  13. package/dist/index.js +17 -17
  14. package/dist/next.d.ts +2 -0
  15. package/dist/performance/FrameRateMonitor.d.ts +85 -0
  16. package/dist/types/character-settings.d.ts +7 -1
  17. package/dist/types/character.d.ts +42 -16
  18. package/dist/types/index.d.ts +165 -45
  19. package/dist/vite.d.ts +19 -0
  20. package/next.d.ts +3 -0
  21. package/next.js +187 -0
  22. package/package.json +38 -8
  23. package/vite.d.ts +20 -0
  24. package/vite.js +126 -0
  25. package/dist/StreamingAudioPlayer-Bq2-bQiT.js +0 -319
  26. package/dist/StreamingAudioPlayer-Bq2-bQiT.js.map +0 -1
  27. package/dist/animation/AnimationWebSocketClient.d.ts +0 -50
  28. package/dist/animation/AnimationWebSocketClient.d.ts.map +0 -1
  29. package/dist/animation/utils/eventEmitter.d.ts +0 -13
  30. package/dist/animation/utils/eventEmitter.d.ts.map +0 -1
  31. package/dist/animation/utils/flameConverter.d.ts +0 -26
  32. package/dist/animation/utils/flameConverter.d.ts.map +0 -1
  33. package/dist/audio/AnimationPlayer.d.ts +0 -57
  34. package/dist/audio/AnimationPlayer.d.ts.map +0 -1
  35. package/dist/audio/StreamingAudioPlayer.d.ts +0 -123
  36. package/dist/audio/StreamingAudioPlayer.d.ts.map +0 -1
  37. package/dist/avatar_core_wasm-D4eEi7Eh.js +0 -1666
  38. package/dist/avatar_core_wasm-D4eEi7Eh.js.map +0 -1
  39. package/dist/avatar_core_wasm.wasm +0 -0
  40. package/dist/config/app-config.d.ts +0 -44
  41. package/dist/config/app-config.d.ts.map +0 -1
  42. package/dist/config/constants.d.ts +0 -29
  43. package/dist/config/constants.d.ts.map +0 -1
  44. package/dist/config/sdk-config-loader.d.ts +0 -12
  45. package/dist/config/sdk-config-loader.d.ts.map +0 -1
  46. package/dist/core/Avatar.d.ts.map +0 -1
  47. package/dist/core/AvatarController.d.ts.map +0 -1
  48. package/dist/core/AvatarDownloader.d.ts +0 -95
  49. package/dist/core/AvatarDownloader.d.ts.map +0 -1
  50. package/dist/core/AvatarKit.d.ts +0 -48
  51. package/dist/core/AvatarKit.d.ts.map +0 -1
  52. package/dist/core/AvatarManager.d.ts.map +0 -1
  53. package/dist/core/AvatarView.d.ts.map +0 -1
  54. package/dist/core/NetworkLayer.d.ts +0 -59
  55. package/dist/core/NetworkLayer.d.ts.map +0 -1
  56. package/dist/generated/driveningress/v1/driveningress.d.ts +0 -80
  57. package/dist/generated/driveningress/v1/driveningress.d.ts.map +0 -1
  58. package/dist/generated/driveningress/v2/driveningress.d.ts +0 -81
  59. package/dist/generated/driveningress/v2/driveningress.d.ts.map +0 -1
  60. package/dist/generated/google/protobuf/struct.d.ts +0 -108
  61. package/dist/generated/google/protobuf/struct.d.ts.map +0 -1
  62. package/dist/generated/google/protobuf/timestamp.d.ts +0 -129
  63. package/dist/generated/google/protobuf/timestamp.d.ts.map +0 -1
  64. package/dist/index-bQnEVIkT.js +0 -5999
  65. package/dist/index-bQnEVIkT.js.map +0 -1
  66. package/dist/index.d.ts.map +0 -1
  67. package/dist/index.js.map +0 -1
  68. package/dist/renderer/RenderSystem.d.ts +0 -79
  69. package/dist/renderer/RenderSystem.d.ts.map +0 -1
  70. package/dist/renderer/covariance.d.ts +0 -13
  71. package/dist/renderer/covariance.d.ts.map +0 -1
  72. package/dist/renderer/renderer.d.ts +0 -8
  73. package/dist/renderer/renderer.d.ts.map +0 -1
  74. package/dist/renderer/sortSplats.d.ts +0 -12
  75. package/dist/renderer/sortSplats.d.ts.map +0 -1
  76. package/dist/renderer/webgl/reorderData.d.ts +0 -14
  77. package/dist/renderer/webgl/reorderData.d.ts.map +0 -1
  78. package/dist/renderer/webgl/webglRenderer.d.ts +0 -66
  79. package/dist/renderer/webgl/webglRenderer.d.ts.map +0 -1
  80. package/dist/renderer/webgpu/webgpuRenderer.d.ts +0 -54
  81. package/dist/renderer/webgpu/webgpuRenderer.d.ts.map +0 -1
  82. package/dist/types/character-settings.d.ts.map +0 -1
  83. package/dist/types/character.d.ts.map +0 -1
  84. package/dist/types/index.d.ts.map +0 -1
  85. package/dist/utils/animation-interpolation.d.ts +0 -17
  86. package/dist/utils/animation-interpolation.d.ts.map +0 -1
  87. package/dist/utils/cls-tracker.d.ts +0 -17
  88. package/dist/utils/cls-tracker.d.ts.map +0 -1
  89. package/dist/utils/error-utils.d.ts +0 -27
  90. package/dist/utils/error-utils.d.ts.map +0 -1
  91. package/dist/utils/logger.d.ts +0 -35
  92. package/dist/utils/logger.d.ts.map +0 -1
  93. package/dist/utils/reqId.d.ts +0 -20
  94. package/dist/utils/reqId.d.ts.map +0 -1
  95. package/dist/wasm/avatarCoreAdapter.d.ts +0 -188
  96. package/dist/wasm/avatarCoreAdapter.d.ts.map +0 -1
  97. package/dist/wasm/avatarCoreMemory.d.ts +0 -141
  98. package/dist/wasm/avatarCoreMemory.d.ts.map +0 -1
@@ -1,319 +0,0 @@
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-bQnEVIkT.js";
5
- class A {
6
- constructor(t) {
7
- // AudioContext is managed internally
8
- i(this, "audioContext", null);
9
- i(this, "sampleRate");
10
- i(this, "channelCount");
11
- i(this, "debug");
12
- // Session-level state
13
- i(this, "sessionId");
14
- i(this, "sessionStartTime", 0);
15
- // AudioContext time when session started
16
- i(this, "pausedTimeOffset", 0);
17
- // Accumulated paused time
18
- i(this, "pausedAt", 0);
19
- // Time when paused
20
- i(this, "scheduledTime", 0);
21
- // Next chunk schedule time in AudioContext time
22
- // Playback state
23
- i(this, "isPlaying", !1);
24
- i(this, "isPaused", !1);
25
- i(this, "autoStartEnabled", !0);
26
- // Control whether to auto-start when buffer is ready
27
- // Audio buffer queue
28
- i(this, "audioChunks", []);
29
- i(this, "scheduledChunks", 0);
30
- // Number of chunks already scheduled
31
- i(this, "activeSources", []);
32
- // Event callbacks
33
- 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;
35
- }
36
- /**
37
- * Initialize audio context (create and ensure it's ready)
38
- */
39
- async initialize() {
40
- if (!this.audioContext)
41
- try {
42
- this.audioContext = new AudioContext({
43
- sampleRate: this.sampleRate
44
- }), this.audioContext.state === "suspended" && await this.audioContext.resume(), this.log("AudioContext initialized", {
45
- sessionId: this.sessionId,
46
- sampleRate: this.audioContext.sampleRate,
47
- state: this.audioContext.state
48
- });
49
- } catch (t) {
50
- const e = m(t);
51
- throw c.logEvent("activeAudioSessionFailed", "warning", {
52
- sessionId: this.sessionId,
53
- reason: e
54
- }), r.error("Failed to initialize AudioContext:", e), t instanceof Error ? t : new Error(e);
55
- }
56
- }
57
- /**
58
- * Add audio chunk (16-bit PCM)
59
- */
60
- addChunk(t, e = !1) {
61
- if (!this.audioContext) {
62
- r.error("AudioContext not initialized");
63
- return;
64
- }
65
- this.audioChunks.push({ data: t, isLast: e }), this.log(`Added chunk ${this.audioChunks.length}`, {
66
- size: t.length,
67
- totalChunks: this.audioChunks.length,
68
- isLast: e,
69
- isPlaying: this.isPlaying,
70
- 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");
72
- }
73
- /**
74
- * Start new session (stop current and start fresh)
75
- */
76
- 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", {
78
- chunks: t.length
79
- });
80
- for (const e of t)
81
- this.addChunk(e.data, e.isLast);
82
- }
83
- /**
84
- * Start playback
85
- */
86
- startPlayback() {
87
- if (!this.audioContext) {
88
- this.log("[StreamingAudioPlayer] Cannot start playback: AudioContext not initialized");
89
- return;
90
- }
91
- if (this.isPlaying) {
92
- this.log("[StreamingAudioPlayer] Cannot start playback: Already playing");
93
- return;
94
- }
95
- this.isPlaying = !0, this.sessionStartTime = this.audioContext.currentTime, this.scheduledTime = this.sessionStartTime, this.log("[StreamingAudioPlayer] Starting playback", {
96
- sessionStartTime: this.sessionStartTime,
97
- bufferedChunks: this.audioChunks.length,
98
- scheduledChunks: this.scheduledChunks,
99
- activeSources: this.activeSources.length
100
- }), this.scheduleAllChunks();
101
- }
102
- /**
103
- * Schedule all pending chunks
104
- */
105
- scheduleAllChunks() {
106
- for (; this.scheduledChunks < this.audioChunks.length; )
107
- this.scheduleNextChunk();
108
- }
109
- /**
110
- * Schedule next audio chunk
111
- */
112
- scheduleNextChunk() {
113
- if (!this.audioContext) {
114
- this.log("[StreamingAudioPlayer] Cannot schedule chunk: AudioContext not initialized");
115
- return;
116
- }
117
- if (!this.isPlaying) {
118
- this.log("[StreamingAudioPlayer] Cannot schedule chunk: Not playing");
119
- return;
120
- }
121
- const t = this.scheduledChunks;
122
- if (t >= this.audioChunks.length) {
123
- this.log(`[StreamingAudioPlayer] No more chunks to schedule (chunkIndex: ${t}, totalChunks: ${this.audioChunks.length})`);
124
- return;
125
- }
126
- const e = this.audioChunks[t];
127
- if (e.data.length === 0 && !e.isLast) {
128
- this.scheduledChunks++;
129
- return;
130
- }
131
- const l = e.data, o = e.isLast, a = this.pcmToAudioBuffer(l);
132
- if (!a) {
133
- r.error("Failed to create AudioBuffer from PCM data"), c.logEvent("character_player", "error", {
134
- sessionId: this.sessionId,
135
- event: "audio_buffer_creation_failed"
136
- });
137
- return;
138
- }
139
- try {
140
- const s = this.audioContext.createBufferSource();
141
- 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
- }, this.scheduledTime += a.duration, this.scheduledChunks++, this.log(`[StreamingAudioPlayer] Scheduled chunk ${t + 1}/${this.audioChunks.length}`, {
145
- startTime: this.scheduledTime - a.duration,
146
- duration: a.duration,
147
- nextScheduleTime: this.scheduledTime,
148
- isLast: o,
149
- activeSources: this.activeSources.length
150
- });
151
- } catch (s) {
152
- r.errorWithError("Failed to schedule audio chunk:", s), c.logEvent("character_player", "error", {
153
- sessionId: this.sessionId,
154
- event: "schedule_chunk_failed",
155
- reason: s instanceof Error ? s.message : String(s)
156
- });
157
- }
158
- }
159
- /**
160
- * Convert PCM data to AudioBuffer
161
- * Input: 16-bit PCM (int16), Output: AudioBuffer (float32 [-1, 1])
162
- */
163
- pcmToAudioBuffer(t) {
164
- if (!this.audioContext)
165
- return null;
166
- if (t.length === 0) {
167
- const u = Math.floor(this.sampleRate * 0.01), n = this.audioContext.createBuffer(
168
- this.channelCount,
169
- u,
170
- this.sampleRate
171
- );
172
- for (let d = 0; d < this.channelCount; d++)
173
- n.getChannelData(d).fill(0);
174
- return n;
175
- }
176
- const e = new Uint8Array(t), l = new Int16Array(e.buffer, 0, e.length / 2), o = l.length / this.channelCount, a = this.audioContext.createBuffer(
177
- this.channelCount,
178
- o,
179
- this.sampleRate
180
- );
181
- 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;
186
- }
187
- }
188
- return a;
189
- }
190
- /**
191
- * Get current playback time (seconds)
192
- */
193
- getCurrentTime() {
194
- if (!this.audioContext || !this.isPlaying)
195
- return 0;
196
- if (this.isPaused)
197
- return this.pausedAt;
198
- const e = this.audioContext.currentTime - this.sessionStartTime - this.pausedTimeOffset;
199
- return Math.max(0, e);
200
- }
201
- /**
202
- * Pause playback
203
- */
204
- pause() {
205
- !this.isPlaying || this.isPaused || (this.isPaused = !0, this.pausedAt = this.getCurrentTime(), this.log("Playback paused", {
206
- pausedAt: this.pausedAt
207
- }));
208
- }
209
- /**
210
- * Resume playback
211
- */
212
- async resume() {
213
- if (!this.isPaused || !this.audioContext)
214
- 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
219
- });
220
- }
221
- /**
222
- * Stop playback
223
- */
224
- stop() {
225
- if (this.audioContext) {
226
- this.isPlaying = !1, this.isPaused = !1, this.sessionStartTime = 0, this.scheduledTime = 0;
227
- for (const t of this.activeSources) {
228
- t.onended = null;
229
- try {
230
- t.stop(0);
231
- } catch {
232
- }
233
- try {
234
- t.disconnect();
235
- } catch {
236
- }
237
- }
238
- this.activeSources = [], this.audioChunks = [], this.scheduledChunks = 0, this.log("[StreamingAudioPlayer] Playback stopped, state reset");
239
- }
240
- }
241
- /**
242
- * Enable or disable auto-start (for delayed start scenarios)
243
- */
244
- setAutoStart(t) {
245
- this.autoStartEnabled = t, this.log(`Auto-start ${t ? "enabled" : "disabled"}`);
246
- }
247
- /**
248
- * Start playback manually (for delayed start scenarios)
249
- * This allows starting playback after transition animation completes
250
- */
251
- play() {
252
- this.isPlaying || (this.autoStartEnabled = !0, this.startPlayback());
253
- }
254
- /**
255
- * Mark playback as ended
256
- */
257
- markEnded() {
258
- var t;
259
- this.log("Playback ended"), this.isPlaying = !1, (t = this.onEndedCallback) == null || t.call(this);
260
- }
261
- /**
262
- * Set ended callback
263
- */
264
- onEnded(t) {
265
- this.onEndedCallback = t;
266
- }
267
- /**
268
- * Check if playing
269
- */
270
- isPlayingNow() {
271
- return this.isPlaying && !this.isPaused;
272
- }
273
- /**
274
- * Get total duration of buffered audio
275
- */
276
- getBufferedDuration() {
277
- if (!this.audioContext)
278
- return 0;
279
- let t = 0;
280
- for (const e of this.audioChunks)
281
- t += e.data.length / 2 / this.channelCount;
282
- return t / this.sampleRate;
283
- }
284
- /**
285
- * Get remaining duration (buffered - played) in seconds
286
- */
287
- getRemainingDuration() {
288
- const t = this.getBufferedDuration(), e = this.getCurrentTime();
289
- return Math.max(0, t - e);
290
- }
291
- /**
292
- * Dispose and cleanup
293
- */
294
- 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");
296
- }
297
- /**
298
- * Flush buffered audio
299
- * - hard: stops all playing sources and clears all chunks
300
- * - soft (default): clears UNSCHEDULED chunks only
301
- */
302
- flush(t) {
303
- if ((t == null ? void 0 : t.hard) === !0) {
304
- this.stop(), this.audioChunks = [], this.scheduledChunks = 0, this.sessionStartTime = 0, this.pausedAt = 0, this.scheduledTime = 0, this.log("Flushed (hard)");
305
- return;
306
- }
307
- this.scheduledChunks < this.audioChunks.length && this.audioChunks.splice(this.scheduledChunks), this.log("Flushed (soft)", { remainingScheduled: this.scheduledChunks });
308
- }
309
- /**
310
- * Debug logging
311
- */
312
- log(t, e) {
313
- this.debug && r.log(`[StreamingAudioPlayer] ${t}`, e || "");
314
- }
315
- }
316
- export {
317
- A as StreamingAudioPlayer
318
- };
319
- //# sourceMappingURL=StreamingAudioPlayer-Bq2-bQiT.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"StreamingAudioPlayer-Bq2-bQiT.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 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\n else if (this.isPlaying) {\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 // 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) {\n this.log('[StreamingAudioPlayer] Cannot schedule chunk: Not playing')\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) {\n return\n }\n\n this.isPaused = true\n this.pausedAt = this.getCurrentTime()\n\n // Don't suspend AudioContext to maintain time continuity\n // Just mark as paused and stop scheduling new chunks\n this.log('Playback paused', {\n pausedAt: this.pausedAt,\n })\n }\n\n /**\n * Resume playback\n */\n async resume(): Promise<void> {\n if (!this.isPaused || !this.audioContext) {\n return\n }\n\n // Calculate paused duration and add to offset\n const pauseDuration = this.audioContext.currentTime - (this.sessionStartTime + this.pausedAt)\n this.pausedTimeOffset += pauseDuration\n\n this.isPaused = false\n\n this.log('Playback resumed', {\n pauseDuration,\n totalPausedOffset: this.pausedTimeOffset,\n })\n }\n\n /**\n * Stop playback\n */\n stop(): void {\n if (!this.audioContext) {\n return\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.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","pauseDuration","enabled","_a","callback","totalSamples","total","played","data"],"mappings":"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"}
@@ -1,50 +0,0 @@
1
- import { EventEmitter } from './utils/eventEmitter';
2
- export interface AnimationWebSocketClientOptions {
3
- wsUrl: string;
4
- reconnectAttempts?: number;
5
- debug?: boolean;
6
- jwtToken?: string;
7
- }
8
- export declare class AnimationWebSocketClient extends EventEmitter {
9
- private wsUrl;
10
- private reconnectAttempts;
11
- private debug;
12
- private jwtToken?;
13
- private ws;
14
- private currentCharacterId;
15
- private currentRetryCount;
16
- private isConnecting;
17
- private isManuallyDisconnected;
18
- private reconnectTimer;
19
- constructor(options: AnimationWebSocketClientOptions);
20
- /**
21
- * 连接WebSocket
22
- */
23
- connect(characterId: string): Promise<void>;
24
- /**
25
- * 断开连接
26
- */
27
- 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
- */
40
- isConnected(): boolean;
41
- /**
42
- * 获取当前角色ID
43
- */
44
- getCurrentCharacterId(): string;
45
- private buildWebSocketUrl;
46
- private connectWebSocket;
47
- private handleMessage;
48
- private scheduleReconnect;
49
- }
50
- //# sourceMappingURL=AnimationWebSocketClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"AnimationWebSocketClient.d.ts","sourceRoot":"","sources":["../../animation/AnimationWebSocketClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAA;AAEnD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,qBAAa,wBAAyB,SAAQ,YAAY;IACxD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,QAAQ,CAAC,CAAQ;IAEzB,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,sBAAsB,CAAiB;IAC/C,OAAO,CAAC,cAAc,CAAsB;gBAEhC,OAAO,EAAE,+BAA+B;IAQpD;;OAEG;IACG,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCjD;;OAEG;IACH,UAAU,IAAI,IAAI;IAmBlB;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO;IAqC3E;;;OAGG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,qBAAqB,IAAI,MAAM;IAM/B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,gBAAgB;IAoExB,OAAO,CAAC,aAAa;IA2CrB,OAAO,CAAC,iBAAiB;CAmB1B"}
@@ -1,13 +0,0 @@
1
- /**
2
- * Simple Event Emitter
3
- */
4
- type EventHandler = (...args: any[]) => void;
5
- export declare class EventEmitter {
6
- private events;
7
- on(event: string, handler: EventHandler): void;
8
- off(event: string, handler: EventHandler): void;
9
- emit(event: string, ...args: any[]): void;
10
- removeAllListeners(event?: string): void;
11
- }
12
- export {};
13
- //# sourceMappingURL=eventEmitter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"eventEmitter.d.ts","sourceRoot":"","sources":["../../../animation/utils/eventEmitter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,KAAK,YAAY,GAAG,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;AAE5C,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAA4C;IAE1D,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI;IAO9C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,IAAI;IAW/C,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;IAOzC,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;CAQzC"}
@@ -1,26 +0,0 @@
1
- import { Flame } from '../../generated/driveningress/v1/driveningress';
2
- export interface FlameParams {
3
- shape_params?: number[];
4
- expr_params?: number[];
5
- rotation?: number[];
6
- translation?: number[];
7
- neck_pose?: number[];
8
- jaw_pose?: number[];
9
- eyes_pose?: number[];
10
- eyelid?: number[];
11
- has_eyelid?: boolean;
12
- }
13
- /**
14
- * Convert proto Flame to WASM FlameParams format
15
- */
16
- 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
- */
21
- export declare function convertWasmParamsToProtoFlame(wasmParams: FlameParams): Flame;
22
- /**
23
- * Create a neutral proto Flame (zero pose)
24
- */
25
- export declare function createNeutralFlameProto(): Flame;
26
- //# sourceMappingURL=flameConverter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"flameConverter.d.ts","sourceRoot":"","sources":["../../../animation/utils/flameConverter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gDAAgD,CAAA;AAE3E,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,KAAK,GAAG,WAAW,CAY5E;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,WAAW,GAAG,KAAK,CAU5E;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,KAAK,CAU/C"}
@@ -1,57 +0,0 @@
1
- import { StreamingAudioPlayer } from './StreamingAudioPlayer';
2
- export declare class AnimationPlayer {
3
- private audio;
4
- private streamingPlayer;
5
- private _isPlaying;
6
- private fps;
7
- private onEndedCallback?;
8
- private static audioUnlocked;
9
- private useStreaming;
10
- /**
11
- * 解锁音频上下文(Safari 自动播放策略)
12
- * 必须在用户交互事件(如 click)中调用
13
- */
14
- static unlockAudioContext(): Promise<void>;
15
- /**
16
- * Initialize with HTMLAudioElement (traditional way)
17
- */
18
- initialize(audioUrl: string, onEnded?: () => void): Promise<void>;
19
- /**
20
- * Initialize with StreamingAudioPlayer (streaming way)
21
- * @deprecated 使用 prepareStreamingPlayer() 代替
22
- */
23
- initializeStreaming(streamingPlayer: StreamingAudioPlayer, onEnded?: () => void): Promise<void>;
24
- /**
25
- * 检查流式播放器是否已准备好
26
- */
27
- isStreamingReady(): boolean;
28
- /**
29
- * 获取流式播放器实例
30
- */
31
- getStreamingPlayer(): StreamingAudioPlayer | null;
32
- /**
33
- * 创建并初始化流式播放器
34
- * 在服务连接建立时调用
35
- */
36
- createAndInitializeStreamingPlayer(): Promise<void>;
37
- /**
38
- * 准备流式播放器(如果未创建则创建并初始化)
39
- * 停止之前的播放并更新结束回调
40
- */
41
- prepareStreamingPlayer(onEnded?: () => void): Promise<void>;
42
- private setupEventListeners;
43
- play(): Promise<void>;
44
- stop(): void;
45
- isPlaying(): boolean;
46
- getCurrentFrameIndex(): number;
47
- /**
48
- * Get current playback time
49
- */
50
- getCurrentTime(): number;
51
- /**
52
- * 添加音频块(仅用于流式播放)
53
- */
54
- addAudioChunk(audio: Uint8Array, isLast?: boolean): void;
55
- dispose(): void;
56
- }
57
- //# sourceMappingURL=AnimationPlayer.d.ts.map
@@ -1 +0,0 @@
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,123 +0,0 @@
1
- /**
2
- * Streaming Audio Player
3
- * Implements real-time audio playback using Web Audio API
4
- * Supports dynamic PCM chunk addition without Workers
5
- */
6
- export interface StreamingAudioPlayerOptions {
7
- sampleRate?: number;
8
- channelCount?: number;
9
- debug?: boolean;
10
- }
11
- export declare class StreamingAudioPlayer {
12
- private audioContext;
13
- private sampleRate;
14
- private channelCount;
15
- private debug;
16
- private sessionId;
17
- private sessionStartTime;
18
- private pausedTimeOffset;
19
- private pausedAt;
20
- private scheduledTime;
21
- private isPlaying;
22
- private isPaused;
23
- private autoStartEnabled;
24
- private audioChunks;
25
- private scheduledChunks;
26
- private activeSources;
27
- private onEndedCallback?;
28
- constructor(options?: StreamingAudioPlayerOptions);
29
- /**
30
- * Initialize audio context (create and ensure it's ready)
31
- */
32
- initialize(): Promise<void>;
33
- /**
34
- * Add audio chunk (16-bit PCM)
35
- */
36
- addChunk(pcmData: Uint8Array, isLast?: boolean): void;
37
- /**
38
- * Start new session (stop current and start fresh)
39
- */
40
- startNewSession(audioChunks: Array<{
41
- data: Uint8Array;
42
- isLast: boolean;
43
- }>): Promise<void>;
44
- /**
45
- * Start playback
46
- */
47
- private startPlayback;
48
- /**
49
- * Schedule all pending chunks
50
- */
51
- private scheduleAllChunks;
52
- /**
53
- * Schedule next audio chunk
54
- */
55
- private scheduleNextChunk;
56
- /**
57
- * Convert PCM data to AudioBuffer
58
- * Input: 16-bit PCM (int16), Output: AudioBuffer (float32 [-1, 1])
59
- */
60
- private pcmToAudioBuffer;
61
- /**
62
- * Get current playback time (seconds)
63
- */
64
- getCurrentTime(): number;
65
- /**
66
- * Pause playback
67
- */
68
- pause(): void;
69
- /**
70
- * Resume playback
71
- */
72
- resume(): Promise<void>;
73
- /**
74
- * Stop playback
75
- */
76
- stop(): void;
77
- /**
78
- * Enable or disable auto-start (for delayed start scenarios)
79
- */
80
- setAutoStart(enabled: boolean): void;
81
- /**
82
- * Start playback manually (for delayed start scenarios)
83
- * This allows starting playback after transition animation completes
84
- */
85
- play(): void;
86
- /**
87
- * Mark playback as ended
88
- */
89
- markEnded(): void;
90
- /**
91
- * Set ended callback
92
- */
93
- onEnded(callback: () => void): void;
94
- /**
95
- * Check if playing
96
- */
97
- isPlayingNow(): boolean;
98
- /**
99
- * Get total duration of buffered audio
100
- */
101
- getBufferedDuration(): number;
102
- /**
103
- * Get remaining duration (buffered - played) in seconds
104
- */
105
- getRemainingDuration(): number;
106
- /**
107
- * Dispose and cleanup
108
- */
109
- dispose(): void;
110
- /**
111
- * Flush buffered audio
112
- * - hard: stops all playing sources and clears all chunks
113
- * - soft (default): clears UNSCHEDULED chunks only
114
- */
115
- flush(options?: {
116
- hard?: boolean;
117
- }): void;
118
- /**
119
- * Debug logging
120
- */
121
- private log;
122
- }
123
- //# sourceMappingURL=StreamingAudioPlayer.d.ts.map
@@ -1 +0,0 @@
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"}