@spatialwalk/avatarkit 1.0.0-beta.4 → 1.0.0-beta.41

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 +378 -2
  2. package/README.md +261 -283
  3. package/dist/StreamingAudioPlayer-BXytpr5T.js +506 -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 +12 -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 +5 -27
  13. package/dist/config/sdk-config-loader.d.ts +2 -9
  14. package/dist/core/Avatar.d.ts +0 -15
  15. package/dist/core/AvatarController.d.ts +35 -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 +24 -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-CRKYjlwp.js +14267 -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 +0 -5
  37. package/dist/types/character.d.ts +3 -21
  38. package/dist/types/index.d.ts +72 -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/heartbeat-manager.d.ts +18 -0
  44. package/dist/utils/id-manager.d.ts +38 -0
  45. package/dist/utils/logger.d.ts +5 -11
  46. package/dist/utils/posthog-tracker.d.ts +11 -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 +11 -97
  50. package/dist/wasm/avatarCoreMemory.d.ts +5 -54
  51. package/package.json +15 -13
  52. package/dist/StreamingAudioPlayer-L87IFoao.js +0 -319
  53. package/dist/StreamingAudioPlayer-L87IFoao.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-BDxVrKwm.js +0 -5942
  77. package/dist/index-BDxVrKwm.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
@@ -1,20 +1,15 @@
1
- /**
2
- * Logger Utility
3
- * Environment-aware logger wrapper
4
- * - Logs normally in test/dev environment
5
- * - In production: silently logs to console
6
- * - Errors and warnings are always reported to CLS (when enabled)
7
- *
8
- * Usage: Replace `console.log()` with `logger.log()`
9
- */
1
+ import { LogLevel as GuiiaiLogLevel } from '@guiiai/logg';
2
+ import { LogLevel } from '../types';
10
3
  export declare const logger: import('@guiiai/logg').Logg;
4
+
5
+ export declare function setLogLevel(level: LogLevel): void;
11
6
  export declare const loggerWithUnknown: {
12
7
  error: (message: string, error?: unknown) => void;
13
8
  warn: (message: string, error?: unknown) => void;
14
9
  useGlobalConfig: () => import('@guiiai/logg').Logg;
15
10
  child: (fields?: Record<string, any>) => import('@guiiai/logg').Logg;
16
11
  withContext: (context: string) => import('@guiiai/logg').Logg;
17
- withLogLevel: (logLevel: import('@guiiai/logg').LogLevel) => import('@guiiai/logg').Logg;
12
+ withLogLevel: (logLevel: GuiiaiLogLevel) => import('@guiiai/logg').Logg;
18
13
  withLogLevelString: (logLevelString: import('@guiiai/logg').LogLevelString) => import('@guiiai/logg').Logg;
19
14
  withFormat: (format: import('@guiiai/logg').Format) => import('@guiiai/logg').Logg;
20
15
  withFields: (fields: Record<string, any>) => import('@guiiai/logg').Logg;
@@ -32,4 +27,3 @@ export declare const loggerWithUnknown: {
32
27
  withTimeFormatter: (fn: (inputDate: Date) => string) => import('@guiiai/logg').Logg;
33
28
  withErrorProcessor: (fn: (err: Error | unknown) => Error | unknown) => import('@guiiai/logg').Logg;
34
29
  };
35
- //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1,11 @@
1
+ import { Environment } from '../types';
2
+
3
+ export declare function initializePostHog(environment: Environment, version: string): void;
4
+
5
+ export declare function trackEvent(event: string, level?: 'debug' | 'info' | 'warning' | 'error', contents?: Record<string, unknown>): void;
6
+
7
+ export declare function sendEventViaBeacon(event: string, level?: 'debug' | 'info' | 'warning' | 'error', contents?: Record<string, unknown>): boolean;
8
+
9
+ export declare function cleanupPostHog(): void;
10
+
11
+ export declare function logEvent(event: string, level?: 'debug' | 'info' | 'warning' | 'error', contents?: Record<string, unknown>): void;
@@ -0,0 +1,5 @@
1
+ export declare function isFirstUse(): boolean;
2
+
3
+ export declare function isDailyActive(): boolean;
4
+
5
+ export declare function getLastActiveDate(): string | null;
@@ -0,0 +1,2 @@
1
+ declare const _default: import('vite').UserConfig;
2
+ export default _default;
@@ -1,4 +1,3 @@
1
- import { AvatarCoreMemoryManager } from './avatarCoreMemory';
2
1
  export interface WasmModuleConfig {
3
2
  baseUrl?: string;
4
3
  [key: string]: unknown;
@@ -46,6 +45,8 @@ export declare class AvatarCoreAdapter {
46
45
  private wasmModule;
47
46
  private memoryManager;
48
47
  private coreHandle;
48
+ private characterHandles;
49
+ private animationHandles;
49
50
  private characterHandle;
50
51
  private animationHandle;
51
52
  private totalFrames;
@@ -58,131 +59,44 @@ export declare class AvatarCoreAdapter {
58
59
  private flameInfo?;
59
60
  private characterInfo?;
60
61
  constructor(options?: AvatarCoreOptions);
61
- /**
62
- * 加载 WASM 模块并设置 API
63
- * 每次都创建全新的 WASM 实例,确保 C++ 内存是干净的
64
- *
65
- * 注意:这里使用动态 import() 导入 WASM 模块。
66
- * Vite 在构建时会自动为 WASM 文件和 JS glue 代码添加 hash(如 avatar_core_wasm-CxWuw7eS.wasm),
67
- * 确保浏览器缓存与版本一致,不会有缓存问题。
68
- *
69
- * Hash 的作用机制:
70
- * - WASM 文件内容变化 → hash 自动变化 → URL 变化 → 浏览器拉取新版本
71
- * - WASM 文件内容不变 → hash 保持不变 → URL 不变 → 浏览器使用缓存
72
- * - Emscripten 生成的 JS 内部会使用 hard-coded 的 hash 路径加载 .wasm 文件
73
- */
74
62
  loadWASMModule(): Promise<void>;
75
- /**
76
- * 验证 WASM 模块功能
77
- */
78
63
  private validateWASMModule;
79
- /**
80
- * 初始化内存视图
81
- */
82
64
  private initializeMemoryViews;
83
- /**
84
- * 设置 C API 函数包装
85
- */
86
65
  private setupCAPIFunctions;
87
- /**
88
- * 读取当前动画帧的 FlameParams(用于过渡或调试)
89
- */
90
- getCurrentFrameParams(frameIndex?: number): Promise<FlameParams>;
91
- /**
92
- * 初始化 Avatar Core 核心
93
- */
66
+ getCurrentFrameParams(frameIndex?: number, characterId?: string): Promise<FlameParams>;
94
67
  private initializeAvatarCore;
95
- /**
96
- * 加载模板资源(从预加载的 ArrayBuffers)
97
- */
98
68
  loadTemplateResourcesFromBuffers(templateResources: Record<string, ArrayBuffer>): Promise<boolean>;
99
- /**
100
- * 加载角色数据(从预加载的 ArrayBuffers)
101
- */
102
- loadCharacterFromBuffers(shapeBuffer: ArrayBuffer, pointCloudBuffer: ArrayBuffer): Promise<boolean>;
69
+ loadCharacterFromBuffers(shapeBuffer: ArrayBuffer, pointCloudBuffer: ArrayBuffer, characterId?: string): Promise<number>;
103
70
  private loadAnimationFromData;
104
- /**
105
- * Load animation from ArrayBuffer (for CDN-based dynamic loading)
106
- */
107
- loadAnimationFromBuffer(animData: ArrayBuffer): Promise<number>;
71
+ loadAnimationFromBuffer(animData: ArrayBuffer, characterId?: string): Promise<number>;
108
72
  switchAnimationFile(animationFile: string): Promise<number>;
109
- /**
110
- * 获取动画总帧数
111
- */
112
- getAnimationTotalFrames(): Promise<number>;
113
- /**
114
- * 从动画中获取指定帧的参数
115
- */
116
- getAnimationFrameParams(frameIndex?: number): Promise<number>;
117
- /**
118
- * 设置眼部追踪配置(对齐 app 实现)
119
- */
73
+ getAnimationTotalFrames(animationHandle?: number): Promise<number>;
74
+ getAnimationFrameParams(frameIndex?: number, characterId?: string): Promise<number>;
120
75
  setEyeTrackingConfig(config: {
121
76
  enabled: boolean;
122
77
  auto_eyelid_adjustment?: boolean;
123
78
  eyelid_threshold?: number;
124
79
  }): Promise<boolean>;
125
- /**
126
- * 设置眼部追踪目标(高级功能)
127
- */
128
80
  setGazeTarget(x: number, y: number, z: number): Promise<boolean>;
129
- /**
130
- * 重置眼部追踪
131
- */
132
81
  resetEyeTracking(): Promise<boolean>;
133
- /**
134
- * 查询 FLAME 模型信息
135
- */
136
82
  private queryFlameInfo;
137
- /**
138
- * 查询角色信息
139
- */
140
83
  private queryCharacterInfo;
141
- /**
142
- * 检查错误码并抛出异常
143
- */
144
84
  private checkError;
145
- /**
146
- * 更新进度回调
147
- */
148
85
  private updateProgress;
149
- /**
150
- * 获取性能指标
151
- */
152
86
  getPerformanceMetrics(): PerformanceMetrics & {
153
- memoryStats?: ReturnType<AvatarCoreMemoryManager['getMemoryStats']>;
154
87
  flameInfo?: FlameInfo;
155
88
  characterInfo?: CharacterInfo;
156
89
  version?: string;
157
90
  };
158
- /**
159
- * 释放当前角色和动画资源(但保留 core)
160
- */
91
+ removeCharacter(characterHandle: number, characterId?: string): void;
161
92
  releaseCurrentCharacter(): void;
162
- /**
163
- * 释放所有资源(包括 core)
164
- */
165
93
  release(): void;
166
- /**
167
- * 兼容性接口:提供与 FlameComplete3DGSManager 相同的接口
168
- */
169
94
  loadFlameModel(_modelData: any): Promise<boolean>;
170
95
  load3DGSData(_original3DGSPoints: any, _binding: any, _flameFaces: any): Promise<boolean>;
171
- /**
172
- * 计算帧并返回 GPU 友好的 flat 格式(零拷贝,协方差预计算)
173
- * 🚀 性能优化版本:
174
- * - C++ 预计算协方差矩阵
175
- * - 零拷贝直接访问 WASM 内存http://localhost:3000/us/
176
- * - 直接输出 GPU 格式 [pos3, color4, cov6]
177
- */
178
96
  computeCompleteFrameFlat(params?: {
179
97
  frameIndex?: number;
180
- }): Promise<Float32Array | null>;
181
- /**
182
- * 计算帧并返回 GPU 友好的 flat 格式(从 FLAME 参数)
183
- * 🔑 用于 Realtime: 接受自定义 FLAME 参数并计算 Splat
184
- */
185
- computeFrameFlatFromParams(flameParams: FlameParams): Promise<Float32Array | null>;
98
+ }, characterHandle?: number): Promise<Float32Array | null>;
99
+ computeFrameFlatFromParams(flameParams: FlameParams, characterHandle?: number): Promise<Float32Array | null>;
100
+ getWASMMemoryMB(): number | null;
186
101
  }
187
102
  export {};
188
- //# sourceMappingURL=avatarCoreAdapter.d.ts.map
@@ -1,9 +1,3 @@
1
- /**
2
- * Avatar Core WASM 内存管理工具
3
- *
4
- * 提供高效的内存分配、数据传递和零拷贝访问功能
5
- * 专门为 avatar_core_wasm 优化
6
- */
7
1
  interface WASMModule {
8
2
  _malloc: (size: number) => number;
9
3
  _free: (ptr: number) => void;
@@ -47,22 +41,13 @@ export declare class AvatarCoreMemoryManager {
47
41
  private allocatedPointers;
48
42
  private structPointers;
49
43
  constructor(wasmModule: WASMModule);
50
- /**
51
- * 分配 WASM 内存并复制数据 - 纯 API 方式
52
- */
53
44
  allocateAndCopy(arrayBuffer: ArrayBuffer): MemoryAllocation;
54
- /**
55
- * 创建 AvatarTemplateConfig 结构体
56
- */
57
45
  createTemplateConfig(resources: TemplateResources): number;
58
- /**
59
- * 创建 AvatarCharacterData 结构体 - 新版 Emscripten 方式
60
- */
61
- createCharacterData(shapeBuffer: ArrayBuffer, plyBuffer: ArrayBuffer): number;
62
- /**
63
- * 读取 AvatarFlameParams 结构体数据
64
- * Used to extract current animation frame parameters from WASM memory
65
- */
46
+ createCharacterData(shapeBuffer: ArrayBuffer, plyBuffer: ArrayBuffer, characterId?: string): {
47
+ dataPtr: number;
48
+ shapePtr: number;
49
+ plyPtr: number;
50
+ };
66
51
  readFlameParams(paramsPtr: number): {
67
52
  shape_params: number[];
68
53
  expr_params: number[];
@@ -74,9 +59,6 @@ export declare class AvatarCoreMemoryManager {
74
59
  eyelid: number[];
75
60
  has_eyelid: boolean;
76
61
  };
77
- /**
78
- * 创建 AvatarFlameParams 结构体 - 新版 Emscripten 方式
79
- */
80
62
  createFlameParams(params: {
81
63
  shape_params?: number[];
82
64
  expr_params?: number[];
@@ -88,25 +70,7 @@ export declare class AvatarCoreMemoryManager {
88
70
  eyelid?: number[];
89
71
  has_eyelid?: boolean;
90
72
  }): number;
91
- /**
92
- * 读取 AvatarSplatPointFlatArray 结构体数据(预计算协方差)
93
- *
94
- * 结构体布局:
95
- * - AvatarSplatPointFlat* points (0-3, 32位指针)
96
- * - uint32_t point_count (4-7)
97
- * - float compute_time_ms (8-11)
98
- *
99
- * 每个点布局 (52 bytes):
100
- * - position[3] (12 bytes)
101
- * - color[4] (16 bytes, RGBA)
102
- * - covariance[6] (24 bytes, 预计算好的协方差矩阵上三角)
103
- *
104
- * ⚠️ 使用 getValue 逐个读取,避免动态内存的 HEAPF32 detachment 问题
105
- */
106
73
  readSplatPointFlatArray(arrayPtr: number): Float32Array | null;
107
- /**
108
- * 读取AvatarMeshData结构体数据
109
- */
110
74
  readMeshData(outputPtr: number): {
111
75
  vertices: Float32Array;
112
76
  vertexCount: number;
@@ -116,21 +80,9 @@ export declare class AvatarCoreMemoryManager {
116
80
  landmarkCount: number;
117
81
  computeTime: number;
118
82
  };
119
- /**
120
- * 释放指定指针的内存
121
- */
122
83
  free(ptr: number): void;
123
- /**
124
- * 释放结构体内存
125
- */
126
84
  freeStruct(name: string): void;
127
- /**
128
- * 清理所有分配的内存
129
- */
130
85
  cleanup(): void;
131
- /**
132
- * 获取内存使用统计
133
- */
134
86
  getMemoryStats(): {
135
87
  allocatedPointers: number;
136
88
  structPointers: number;
@@ -138,4 +90,3 @@ export declare class AvatarCoreMemoryManager {
138
90
  };
139
91
  }
140
92
  export {};
141
- //# sourceMappingURL=avatarCoreMemory.d.ts.map
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "@spatialwalk/avatarkit",
3
3
  "type": "module",
4
- "version": "1.0.0-beta.4",
5
- "packageManager": "pnpm@10.18.2",
4
+ "version": "1.0.0-beta.41",
6
5
  "description": "SPAvatar SDK - 3D Gaussian Splatting Avatar Rendering SDK",
7
6
  "author": "SPAvatar Team",
8
7
  "license": "MIT",
@@ -32,28 +31,31 @@
32
31
  "CHANGELOG.md",
33
32
  "dist"
34
33
  ],
35
- "scripts": {
36
- "build": "SDK_BUILD=true vite build --mode library",
37
- "dev": "vite build --mode library --watch",
38
- "clean": "rm -rf dist",
39
- "typecheck": "tsc --noEmit",
40
- "test": "cd tests && pnpm test",
41
- "test:watch": "cd tests && pnpm run test:watch",
42
- "test:e2e": "cd tests && pnpm run test:e2e"
43
- },
44
34
  "peerDependencies": {
45
35
  "@webgpu/types": "*"
46
36
  },
47
37
  "dependencies": {
48
38
  "@bufbuild/protobuf": "^2.10.0",
49
39
  "@guiiai/logg": "^1.2.4",
50
- "nanoid": "^5.1.6"
40
+ "nanoid": "^5.1.6",
41
+ "posthog-js": "^1.310.1"
51
42
  },
52
43
  "devDependencies": {
53
44
  "@types/node": "^20.11.30",
54
45
  "@webgpu/types": "^0.1.65",
46
+ "tsx": "^4.20.6",
55
47
  "typescript": "^5.0.0",
56
48
  "vite": "^5.0.0",
57
49
  "vite-plugin-dts": "^4.5.4"
50
+ },
51
+ "scripts": {
52
+ "build": "SDK_BUILD=true vite build --mode library",
53
+ "dev": "vite build --mode library --watch",
54
+ "clean": "rm -rf dist",
55
+ "typecheck": "tsc --noEmit",
56
+ "test": "cd tests && pnpm test",
57
+ "test:watch": "cd tests && pnpm run test:watch",
58
+ "test:e2e": "cd tests && pnpm run test:e2e",
59
+ "test:perf": "cd tests && pnpm run test:perf"
58
60
  }
59
- }
61
+ }
@@ -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-BDxVrKwm.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-L87IFoao.js.map