@spatialwalk/avatarkit 1.0.0-beta.62 → 1.0.0-beta.64
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 +40 -0
- package/README.md +136 -35
- package/dist/{StreamingAudioPlayer-BWsAt_s7.js → StreamingAudioPlayer-CugAsNXV.js} +1 -1
- package/dist/avatar_core_wasm-Dv943JJl.js +2696 -0
- package/dist/avatar_core_wasm.wasm +0 -0
- package/dist/core/AvatarController.d.ts +2 -0
- package/dist/core/AvatarView.d.ts +1 -0
- package/dist/{index-Bhjn1nq3.js → index-D0sXXlqd.js} +197 -94
- package/dist/index.js +3 -3
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/animation-interpolation.d.ts +4 -0
- package/dist/vanilla/vite.config.d.ts +1 -1
- package/dist/vite.d.ts +4 -0
- package/package.json +23 -14
- package/vite.d.ts +20 -0
- package/vite.js +110 -0
- package/dist/avatar_core_wasm-i0Ocpx6q.js +0 -2693
|
Binary file
|
|
@@ -44,6 +44,8 @@ export declare class AvatarController {
|
|
|
44
44
|
playbackMode?: DrivingServiceMode;
|
|
45
45
|
});
|
|
46
46
|
getCurrentConversationId(): string | null;
|
|
47
|
+
initializeAudioContext(): Promise<void>;
|
|
48
|
+
private checkAudioContextInitialized;
|
|
47
49
|
start(): Promise<void>;
|
|
48
50
|
send(audioData: ArrayBuffer, end?: boolean): string | null;
|
|
49
51
|
close(): void;
|
|
@@ -7173,11 +7173,11 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
7173
7173
|
ErrorCode2["failedToDownloadAvatarAssets"] = "failedToDownloadAvatarAssets";
|
|
7174
7174
|
return ErrorCode2;
|
|
7175
7175
|
})(ErrorCode || {});
|
|
7176
|
-
class
|
|
7176
|
+
class AvatarError extends Error {
|
|
7177
7177
|
constructor(message, code) {
|
|
7178
7178
|
super(message);
|
|
7179
7179
|
this.code = code;
|
|
7180
|
-
this.name = "
|
|
7180
|
+
this.name = "AvatarError";
|
|
7181
7181
|
}
|
|
7182
7182
|
}
|
|
7183
7183
|
{
|
|
@@ -7624,7 +7624,7 @@ const _AnimationPlayer = class _AnimationPlayer {
|
|
|
7624
7624
|
if (this.streamingPlayer) {
|
|
7625
7625
|
return;
|
|
7626
7626
|
}
|
|
7627
|
-
const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-
|
|
7627
|
+
const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-CugAsNXV.js");
|
|
7628
7628
|
const { AvatarSDK: AvatarSDK2 } = await Promise.resolve().then(() => AvatarSDK$1);
|
|
7629
7629
|
const audioFormat = AvatarSDK2.getAudioFormat();
|
|
7630
7630
|
this.streamingPlayer = new StreamingAudioPlayer({
|
|
@@ -8180,7 +8180,7 @@ class AvatarCoreAdapter {
|
|
|
8180
8180
|
}
|
|
8181
8181
|
async loadWASMModule() {
|
|
8182
8182
|
try {
|
|
8183
|
-
const { default: AvatarCoreModule } = await import("./avatar_core_wasm-
|
|
8183
|
+
const { default: AvatarCoreModule } = await import("./avatar_core_wasm-Dv943JJl.js");
|
|
8184
8184
|
if (this.wasmConfig && Object.keys(this.wasmConfig).length > 0) {
|
|
8185
8185
|
this.wasmModule = await AvatarCoreModule(this.wasmConfig);
|
|
8186
8186
|
} else {
|
|
@@ -8961,7 +8961,7 @@ class AvatarSDK {
|
|
|
8961
8961
|
}
|
|
8962
8962
|
__publicField(AvatarSDK, "_isInitialized", false);
|
|
8963
8963
|
__publicField(AvatarSDK, "_configuration", null);
|
|
8964
|
-
__publicField(AvatarSDK, "_version", "1.0.0-beta.
|
|
8964
|
+
__publicField(AvatarSDK, "_version", "1.0.0-beta.64");
|
|
8965
8965
|
__publicField(AvatarSDK, "_avatarCore", null);
|
|
8966
8966
|
__publicField(AvatarSDK, "_dynamicSdkConfig", null);
|
|
8967
8967
|
const AvatarSDK$1 = Object.freeze(Object.defineProperty({
|
|
@@ -10156,7 +10156,7 @@ class AnimationWebSocketClient extends EventEmitter {
|
|
|
10156
10156
|
});
|
|
10157
10157
|
}
|
|
10158
10158
|
if (sdkErrorCode) {
|
|
10159
|
-
const error = new
|
|
10159
|
+
const error = new AvatarError(
|
|
10160
10160
|
reason || `WebSocket connection failed with code ${event.code}`,
|
|
10161
10161
|
sdkErrorCode
|
|
10162
10162
|
);
|
|
@@ -10313,7 +10313,7 @@ class NetworkLayer {
|
|
|
10313
10313
|
var _a, _b, _c, _d;
|
|
10314
10314
|
if (this.isConnecting) {
|
|
10315
10315
|
logger.warn("[NetworkLayer] Connection already in progress, waiting for current connection to complete");
|
|
10316
|
-
throw new
|
|
10316
|
+
throw new AvatarError("Connection already in progress", "CONNECTION_IN_PROGRESS");
|
|
10317
10317
|
}
|
|
10318
10318
|
(_b = (_a = this.dataController).onConnectionState) == null ? void 0 : _b.call(_a, ConnectionState.connecting);
|
|
10319
10319
|
this.isFallbackMode = false;
|
|
@@ -10391,7 +10391,7 @@ class NetworkLayer {
|
|
|
10391
10391
|
const sent = this.wsClient.sendAudioData(this.currentConversationId, audioData, isLast);
|
|
10392
10392
|
if (!sent) {
|
|
10393
10393
|
logger.error("[NetworkLayer] Failed to send audio data to WebSocket");
|
|
10394
|
-
(_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new
|
|
10394
|
+
(_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new AvatarError("Failed to send audio data to WebSocket", "SEND_FAILED"));
|
|
10395
10395
|
logEvent("send_audio_failed", "error", {
|
|
10396
10396
|
req_id: this.currentConversationId || "",
|
|
10397
10397
|
con_id: idManager.getConnectionId() || "",
|
|
@@ -10549,7 +10549,7 @@ class NetworkLayer {
|
|
|
10549
10549
|
} else {
|
|
10550
10550
|
sdkErrorCode = errorCodeStr || "SERVER_ERROR";
|
|
10551
10551
|
}
|
|
10552
|
-
(_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new
|
|
10552
|
+
(_b = (_a = this.dataController).onError) == null ? void 0 : _b.call(_a, new AvatarError(
|
|
10553
10553
|
errorMessage,
|
|
10554
10554
|
sdkErrorCode
|
|
10555
10555
|
));
|
|
@@ -10741,38 +10741,71 @@ class AvatarController {
|
|
|
10741
10741
|
getCurrentConversationId() {
|
|
10742
10742
|
return this.getEffectiveConversationId();
|
|
10743
10743
|
}
|
|
10744
|
-
async
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
"NETWORK_LAYER_NOT_AVAILABLE"
|
|
10749
|
-
);
|
|
10744
|
+
async initializeAudioContext() {
|
|
10745
|
+
var _a;
|
|
10746
|
+
if ((_a = this.animationPlayer) == null ? void 0 : _a.isStreamingReady()) {
|
|
10747
|
+
return;
|
|
10750
10748
|
}
|
|
10751
10749
|
if (!this.animationPlayer) {
|
|
10752
10750
|
this.animationPlayer = new AnimationPlayer();
|
|
10751
|
+
}
|
|
10752
|
+
if (!this.animationPlayer.isStreamingReady()) {
|
|
10753
10753
|
try {
|
|
10754
10754
|
await this.animationPlayer.createAndInitializeStreamingPlayer();
|
|
10755
10755
|
} catch (error) {
|
|
10756
10756
|
const message = error instanceof Error ? error.message : String(error);
|
|
10757
|
-
logger.error("[AvatarController] Failed to
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10761
|
-
|
|
10762
|
-
});
|
|
10763
|
-
throw error;
|
|
10757
|
+
logger.error("[AvatarController] Failed to initialize audio context:", message);
|
|
10758
|
+
throw new AvatarError(
|
|
10759
|
+
`Failed to initialize audio context: ${message}`,
|
|
10760
|
+
"AUDIO_CONTEXT_INIT_FAILED"
|
|
10761
|
+
);
|
|
10764
10762
|
}
|
|
10765
10763
|
}
|
|
10764
|
+
const streamingPlayer = this.animationPlayer.getStreamingPlayer();
|
|
10765
|
+
if (streamingPlayer) {
|
|
10766
|
+
const audioContext = streamingPlayer.audioContext;
|
|
10767
|
+
if (audioContext && audioContext.state === "suspended") {
|
|
10768
|
+
try {
|
|
10769
|
+
await audioContext.resume();
|
|
10770
|
+
} catch (err) {
|
|
10771
|
+
logger.warn("[AvatarController] Failed to resume AudioContext during initialization:", err);
|
|
10772
|
+
}
|
|
10773
|
+
}
|
|
10774
|
+
}
|
|
10775
|
+
}
|
|
10776
|
+
checkAudioContextInitialized() {
|
|
10777
|
+
var _a;
|
|
10778
|
+
if (!((_a = this.animationPlayer) == null ? void 0 : _a.isStreamingReady())) {
|
|
10779
|
+
throw new AvatarError(
|
|
10780
|
+
"Audio context not initialized. Call initializeAudioContext() in a user gesture context first.",
|
|
10781
|
+
"AUDIO_CONTEXT_NOT_INITIALIZED"
|
|
10782
|
+
);
|
|
10783
|
+
}
|
|
10784
|
+
}
|
|
10785
|
+
async start() {
|
|
10786
|
+
if (!this.networkLayer) {
|
|
10787
|
+
throw new AvatarError(
|
|
10788
|
+
"Network layer not available. Use SDK mode.",
|
|
10789
|
+
"NETWORK_LAYER_NOT_AVAILABLE"
|
|
10790
|
+
);
|
|
10791
|
+
}
|
|
10792
|
+
this.checkAudioContextInitialized();
|
|
10766
10793
|
await this.networkLayer.connect(this.avatar.id);
|
|
10767
10794
|
}
|
|
10768
10795
|
send(audioData, end = false) {
|
|
10769
|
-
var _a, _b, _c;
|
|
10796
|
+
var _a, _b, _c, _d;
|
|
10797
|
+
try {
|
|
10798
|
+
this.checkAudioContextInitialized();
|
|
10799
|
+
} catch (error) {
|
|
10800
|
+
(_a = this.onError) == null ? void 0 : _a.call(this, error);
|
|
10801
|
+
return null;
|
|
10802
|
+
}
|
|
10770
10803
|
if (!this.networkLayer) {
|
|
10771
|
-
(
|
|
10804
|
+
(_b = this.onError) == null ? void 0 : _b.call(this, new AvatarError("Network layer not available", "NETWORK_LAYER_NOT_AVAILABLE"));
|
|
10772
10805
|
return null;
|
|
10773
10806
|
}
|
|
10774
10807
|
if (!this.networkLayer.canSend()) {
|
|
10775
|
-
(
|
|
10808
|
+
(_c = this.onError) == null ? void 0 : _c.call(this, new AvatarError("Service not connected", "NOT_CONNECTED"));
|
|
10776
10809
|
logEvent("character_manager", "warning", {
|
|
10777
10810
|
avatar_id: this.avatar.id,
|
|
10778
10811
|
event: "send_not_connected"
|
|
@@ -10790,7 +10823,7 @@ class AvatarController {
|
|
|
10790
10823
|
}
|
|
10791
10824
|
if (!this.isPlaying && this.currentState === AvatarState.idle) {
|
|
10792
10825
|
this.currentState = AvatarState.active;
|
|
10793
|
-
(
|
|
10826
|
+
(_d = this.onConversationState) == null ? void 0 : _d.call(this, this.mapToConversationState(AvatarState.active));
|
|
10794
10827
|
}
|
|
10795
10828
|
return this.networkLayer.getCurrentConversationId();
|
|
10796
10829
|
}
|
|
@@ -10810,18 +10843,13 @@ class AvatarController {
|
|
|
10810
10843
|
(_a = this.onConnectionState) == null ? void 0 : _a.call(this, ConnectionState.disconnected);
|
|
10811
10844
|
}
|
|
10812
10845
|
async playback(initialAudioChunks, initialKeyframes) {
|
|
10846
|
+
this.checkAudioContextInitialized();
|
|
10813
10847
|
if (this.isPlaying || this.currentConversationId) {
|
|
10814
10848
|
this.interrupt();
|
|
10815
10849
|
}
|
|
10816
10850
|
this.currentConversationId = this.generateAndLogNewConversationId();
|
|
10817
10851
|
this.reqEnd = false;
|
|
10818
10852
|
this.clearPlaybackData();
|
|
10819
|
-
if (!this.animationPlayer) {
|
|
10820
|
-
this.animationPlayer = new AnimationPlayer();
|
|
10821
|
-
}
|
|
10822
|
-
if (!this.animationPlayer.isStreamingReady()) {
|
|
10823
|
-
await this.animationPlayer.createAndInitializeStreamingPlayer();
|
|
10824
|
-
}
|
|
10825
10853
|
if (initialAudioChunks && initialAudioChunks.length > 0) {
|
|
10826
10854
|
this.pendingAudioChunks.push(...initialAudioChunks);
|
|
10827
10855
|
}
|
|
@@ -10838,7 +10866,7 @@ class AvatarController {
|
|
|
10838
10866
|
this.isAudioOnlyMode = true;
|
|
10839
10867
|
}
|
|
10840
10868
|
if (this.pendingAudioChunks.length === 0) {
|
|
10841
|
-
throw new
|
|
10869
|
+
throw new AvatarError("No audio chunks to play", "NO_AUDIO");
|
|
10842
10870
|
}
|
|
10843
10871
|
if (this.isAudioOnlyMode) {
|
|
10844
10872
|
await this.startAudioOnlyPlayback();
|
|
@@ -10848,7 +10876,13 @@ class AvatarController {
|
|
|
10848
10876
|
return this.currentConversationId;
|
|
10849
10877
|
}
|
|
10850
10878
|
yieldAudioData(data, isLast = false) {
|
|
10851
|
-
var _a, _b;
|
|
10879
|
+
var _a, _b, _c;
|
|
10880
|
+
try {
|
|
10881
|
+
this.checkAudioContextInitialized();
|
|
10882
|
+
} catch (error) {
|
|
10883
|
+
(_a = this.onError) == null ? void 0 : _a.call(this, error);
|
|
10884
|
+
return null;
|
|
10885
|
+
}
|
|
10852
10886
|
if (this.reqEnd && this.isPlaying && this.currentConversationId) {
|
|
10853
10887
|
this.interrupt();
|
|
10854
10888
|
this.currentConversationId = this.generateAndLogNewConversationId();
|
|
@@ -10879,12 +10913,12 @@ class AvatarController {
|
|
|
10879
10913
|
metrics.tap2Timestamp = Date.now();
|
|
10880
10914
|
}
|
|
10881
10915
|
}
|
|
10882
|
-
if (this.isPlaying && ((
|
|
10916
|
+
if (this.isPlaying && ((_b = this.animationPlayer) == null ? void 0 : _b.isStreamingReady())) {
|
|
10883
10917
|
this.animationPlayer.addAudioChunk(data, isLast);
|
|
10884
10918
|
} else {
|
|
10885
10919
|
if (data.length > 0 || isLast) {
|
|
10886
10920
|
this.pendingAudioChunks.push({ data, isLast });
|
|
10887
|
-
(
|
|
10921
|
+
(_c = this.onConversationState) == null ? void 0 : _c.call(this, this.mapToConversationState(AvatarState.active));
|
|
10888
10922
|
}
|
|
10889
10923
|
}
|
|
10890
10924
|
return this.currentConversationId;
|
|
@@ -10988,7 +11022,7 @@ class AvatarController {
|
|
|
10988
11022
|
var _a;
|
|
10989
11023
|
this.isStartingPlayback = false;
|
|
10990
11024
|
logger.error("[AvatarController] Failed to auto-start playback:", error);
|
|
10991
|
-
(_a = this.onError) == null ? void 0 : _a.call(this, new
|
|
11025
|
+
(_a = this.onError) == null ? void 0 : _a.call(this, new AvatarError("Failed to start playback", "PLAYBACK_START_FAILED"));
|
|
10992
11026
|
});
|
|
10993
11027
|
}
|
|
10994
11028
|
}
|
|
@@ -11237,6 +11271,7 @@ class AvatarController {
|
|
|
11237
11271
|
}
|
|
11238
11272
|
async startStreamingPlaybackInternal() {
|
|
11239
11273
|
var _a, _b, _c;
|
|
11274
|
+
this.checkAudioContextInitialized();
|
|
11240
11275
|
if (this.isPlaying) {
|
|
11241
11276
|
this.isStartingPlayback = false;
|
|
11242
11277
|
return;
|
|
@@ -11245,30 +11280,15 @@ class AvatarController {
|
|
|
11245
11280
|
return;
|
|
11246
11281
|
}
|
|
11247
11282
|
this.isStartingPlayback = true;
|
|
11248
|
-
if (!this.animationPlayer) {
|
|
11249
|
-
this.animationPlayer = new AnimationPlayer();
|
|
11250
|
-
}
|
|
11251
|
-
if (!this.animationPlayer.isStreamingReady()) {
|
|
11252
|
-
try {
|
|
11253
|
-
await this.animationPlayer.createAndInitializeStreamingPlayer();
|
|
11254
|
-
} catch (error) {
|
|
11255
|
-
this.isStartingPlayback = false;
|
|
11256
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
11257
|
-
logger.error("[AvatarController] Failed to create streaming player:", message);
|
|
11258
|
-
logEvent("character_player", "error", {
|
|
11259
|
-
avatar_id: this.avatar.id,
|
|
11260
|
-
event: "streaming_player_init_failed",
|
|
11261
|
-
reason: message
|
|
11262
|
-
});
|
|
11263
|
-
throw error;
|
|
11264
|
-
}
|
|
11265
|
-
}
|
|
11266
11283
|
if (!this.currentKeyframes || this.currentKeyframes.length === 0) {
|
|
11267
11284
|
this.isStartingPlayback = false;
|
|
11268
11285
|
logger.warn("[AvatarController] No animation data to play");
|
|
11269
11286
|
return;
|
|
11270
11287
|
}
|
|
11271
11288
|
try {
|
|
11289
|
+
if (!this.animationPlayer) {
|
|
11290
|
+
throw new AvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
|
|
11291
|
+
}
|
|
11272
11292
|
await this.animationPlayer.prepareStreamingPlayer(() => {
|
|
11273
11293
|
var _a2, _b2;
|
|
11274
11294
|
this.isPlaying = false;
|
|
@@ -11311,7 +11331,7 @@ class AvatarController {
|
|
|
11311
11331
|
} catch (error) {
|
|
11312
11332
|
const message = error instanceof Error ? error.message : String(error);
|
|
11313
11333
|
logger.error("[AvatarController] Failed to start streaming playback:", message);
|
|
11314
|
-
(_c = this.onError) == null ? void 0 : _c.call(this, new
|
|
11334
|
+
(_c = this.onError) == null ? void 0 : _c.call(this, new AvatarError("Failed to start streaming playback", "INIT_FAILED"));
|
|
11315
11335
|
this.isPlaying = false;
|
|
11316
11336
|
this.isStartingPlayback = false;
|
|
11317
11337
|
}
|
|
@@ -11490,21 +11510,7 @@ class AvatarController {
|
|
|
11490
11510
|
async startAudioOnlyPlayback() {
|
|
11491
11511
|
var _a, _b;
|
|
11492
11512
|
if (!this.animationPlayer) {
|
|
11493
|
-
|
|
11494
|
-
}
|
|
11495
|
-
if (!this.animationPlayer.isStreamingReady()) {
|
|
11496
|
-
try {
|
|
11497
|
-
await this.animationPlayer.createAndInitializeStreamingPlayer();
|
|
11498
|
-
} catch (error) {
|
|
11499
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
11500
|
-
logger.error("[AvatarController] Failed to create streaming player for audio-only mode:", message);
|
|
11501
|
-
logEvent("character_player", "error", {
|
|
11502
|
-
avatar_id: this.avatar.id,
|
|
11503
|
-
event: "audio_only_streaming_player_init_failed",
|
|
11504
|
-
reason: message
|
|
11505
|
-
});
|
|
11506
|
-
throw error;
|
|
11507
|
-
}
|
|
11513
|
+
throw new AvatarError("Animation player not initialized", "ANIMATION_PLAYER_NOT_INITIALIZED");
|
|
11508
11514
|
}
|
|
11509
11515
|
try {
|
|
11510
11516
|
await this.animationPlayer.prepareStreamingPlayer(() => {
|
|
@@ -11541,7 +11547,7 @@ class AvatarController {
|
|
|
11541
11547
|
} catch (error) {
|
|
11542
11548
|
const message = error instanceof Error ? error.message : String(error);
|
|
11543
11549
|
logger.error("[AvatarController] Failed to start audio-only playback:", message);
|
|
11544
|
-
(_b = this.onError) == null ? void 0 : _b.call(this, new
|
|
11550
|
+
(_b = this.onError) == null ? void 0 : _b.call(this, new AvatarError("Failed to start audio-only playback", "AUDIO_ONLY_INIT_FAILED"));
|
|
11545
11551
|
this.isPlaying = false;
|
|
11546
11552
|
this.isAudioOnlyMode = false;
|
|
11547
11553
|
throw error;
|
|
@@ -11587,7 +11593,9 @@ class AvatarController {
|
|
|
11587
11593
|
}
|
|
11588
11594
|
addAudioChunkToBuffer(data, isLast) {
|
|
11589
11595
|
if (!this.animationPlayer) {
|
|
11590
|
-
|
|
11596
|
+
logger.warn("[AvatarController] animationPlayer is null in addAudioChunkToBuffer, this should not happen");
|
|
11597
|
+
this.pendingAudioChunks.push({ data, isLast });
|
|
11598
|
+
return;
|
|
11591
11599
|
}
|
|
11592
11600
|
if (this.isPlaying && this.animationPlayer.isStreamingReady()) {
|
|
11593
11601
|
this.animationPlayer.addAudioChunk(data, isLast);
|
|
@@ -12088,9 +12096,10 @@ class AvatarDownloader {
|
|
|
12088
12096
|
const idleAnimationUrl = (_i2 = (_h = (_g = characterMeta.animations) == null ? void 0 : _g.frameIdle) == null ? void 0 : _h.resource) == null ? void 0 : _i2.remote;
|
|
12089
12097
|
if (!shapeUrl || !pointCloudUrl) {
|
|
12090
12098
|
const reason = "Missing required resources: shape or gsStandard (point cloud)";
|
|
12091
|
-
logEvent("
|
|
12099
|
+
logEvent("character_load", "error", {
|
|
12092
12100
|
avatar_id: characterMeta.characterId ?? "unknown",
|
|
12093
|
-
description:
|
|
12101
|
+
description: "Character does not exist in target environment",
|
|
12102
|
+
reason
|
|
12094
12103
|
});
|
|
12095
12104
|
throw new Error(reason);
|
|
12096
12105
|
}
|
|
@@ -12138,6 +12147,14 @@ class AvatarDownloader {
|
|
|
12138
12147
|
return { key, success: true, size: arrayBuffer.byteLength };
|
|
12139
12148
|
} catch (error) {
|
|
12140
12149
|
if (!optional) {
|
|
12150
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
12151
|
+
logEvent("download_avatar_assets_failed", "error", {
|
|
12152
|
+
avatar_id: characterMeta.characterId ?? "unknown",
|
|
12153
|
+
description: `Failed to download required resource: ${filename}`,
|
|
12154
|
+
resource: key,
|
|
12155
|
+
url,
|
|
12156
|
+
error: errorMessage
|
|
12157
|
+
});
|
|
12141
12158
|
throw error;
|
|
12142
12159
|
}
|
|
12143
12160
|
logger.warn(`⚠️ Optional resource ${filename} failed to load:`, error);
|
|
@@ -12224,7 +12241,7 @@ class AvatarDownloader {
|
|
|
12224
12241
|
avatar_id: extractedCharacterId,
|
|
12225
12242
|
description: errorMessage
|
|
12226
12243
|
});
|
|
12227
|
-
error = new
|
|
12244
|
+
error = new AvatarError(errorMessage, ErrorCode.avatarIDUnrecognized);
|
|
12228
12245
|
} else {
|
|
12229
12246
|
error = new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
12230
12247
|
}
|
|
@@ -12254,7 +12271,7 @@ class AvatarDownloader {
|
|
|
12254
12271
|
return response;
|
|
12255
12272
|
} catch (error) {
|
|
12256
12273
|
logger.errorWithError("Failed to fetch character:", error);
|
|
12257
|
-
if (error instanceof
|
|
12274
|
+
if (error instanceof AvatarError) {
|
|
12258
12275
|
logEvent("fetch_avatar_metadata_failed", "error", {
|
|
12259
12276
|
avatar_id: characterId ?? "unknown",
|
|
12260
12277
|
description: error.message
|
|
@@ -12380,10 +12397,6 @@ const _AvatarManager = class _AvatarManager {
|
|
|
12380
12397
|
} catch (error) {
|
|
12381
12398
|
const message = error instanceof Error ? error.message : String(error);
|
|
12382
12399
|
logger.error("Failed to load avatar:", message);
|
|
12383
|
-
logEvent("character_load", "error", {
|
|
12384
|
-
avatar_id: id,
|
|
12385
|
-
reason: message
|
|
12386
|
-
});
|
|
12387
12400
|
onProgress == null ? void 0 : onProgress({ type: LoadProgress.failed, error });
|
|
12388
12401
|
throw error;
|
|
12389
12402
|
}
|
|
@@ -13852,6 +13865,38 @@ function lerpArrays(from, to2, progress) {
|
|
|
13852
13865
|
return result2;
|
|
13853
13866
|
}
|
|
13854
13867
|
const clamp01 = (x2) => Math.max(0, Math.min(1, x2));
|
|
13868
|
+
function linearLerp(from, to2, progress) {
|
|
13869
|
+
return {
|
|
13870
|
+
translation: lerpArrays(from.translation || [0, 0, 0], to2.translation || [0, 0, 0], progress),
|
|
13871
|
+
rotation: lerpArrays(from.rotation || [0, 0, 0], to2.rotation || [0, 0, 0], progress),
|
|
13872
|
+
neckPose: lerpArrays(from.neckPose || [0, 0, 0], to2.neckPose || [0, 0, 0], progress),
|
|
13873
|
+
jawPose: lerpArrays(from.jawPose || [0, 0, 0], to2.jawPose || [0, 0, 0], progress),
|
|
13874
|
+
eyePose: lerpArrays(from.eyePose || [0, 0, 0, 0, 0, 0], to2.eyePose || [0, 0, 0, 0, 0, 0], progress),
|
|
13875
|
+
eyeLid: (() => {
|
|
13876
|
+
const fromEyelid = from.eyeLid;
|
|
13877
|
+
const toEyelid = to2.eyeLid;
|
|
13878
|
+
if ((fromEyelid == null ? void 0 : fromEyelid.length) && (toEyelid == null ? void 0 : toEyelid.length))
|
|
13879
|
+
return lerpArrays(fromEyelid, toEyelid, progress);
|
|
13880
|
+
return fromEyelid || toEyelid || [];
|
|
13881
|
+
})(),
|
|
13882
|
+
expression: lerpArrays(from.expression || [], to2.expression || [], progress)
|
|
13883
|
+
};
|
|
13884
|
+
}
|
|
13885
|
+
function generateTransitionFramesLinear(from, to2, durationMs, fps = 25) {
|
|
13886
|
+
const steps = Math.max(1, Math.floor(durationMs / 1e3 * fps));
|
|
13887
|
+
const frames = Array.from({ length: steps });
|
|
13888
|
+
if (steps === 1) {
|
|
13889
|
+
frames[0] = to2;
|
|
13890
|
+
return frames;
|
|
13891
|
+
}
|
|
13892
|
+
for (let i2 = 0; i2 < steps; i2++) {
|
|
13893
|
+
const progress = i2 / (steps - 1);
|
|
13894
|
+
frames[i2] = linearLerp(from, to2, progress);
|
|
13895
|
+
}
|
|
13896
|
+
frames[0] = from;
|
|
13897
|
+
frames[frames.length - 1] = to2;
|
|
13898
|
+
return frames;
|
|
13899
|
+
}
|
|
13855
13900
|
function createBezierEasing(x1, y1, x2, y2) {
|
|
13856
13901
|
const cx = 3 * x1;
|
|
13857
13902
|
const bx = 3 * (x2 - x1) - cx;
|
|
@@ -13955,6 +14000,7 @@ class AvatarView {
|
|
|
13955
14000
|
__publicField(this, "endTransitionDurationMs", 1600);
|
|
13956
14001
|
__publicField(this, "cachedIdleFirstFrame", null);
|
|
13957
14002
|
__publicField(this, "idleCurrentFrameIndex", 0);
|
|
14003
|
+
__publicField(this, "currentPlayingFrame", null);
|
|
13958
14004
|
__publicField(this, "characterHandle", null);
|
|
13959
14005
|
__publicField(this, "characterId");
|
|
13960
14006
|
__publicField(this, "isPureRenderingMode", false);
|
|
@@ -14009,9 +14055,14 @@ class AvatarView {
|
|
|
14009
14055
|
toFixed.expression = ensureLen(toFixed.expression, exprLen);
|
|
14010
14056
|
return { from: fromFixed, to: toFixed };
|
|
14011
14057
|
}
|
|
14012
|
-
generateAndAlignTransitionFrames(from, to2, durationMs) {
|
|
14058
|
+
generateAndAlignTransitionFrames(from, to2, durationMs, useLinearInterpolation = false) {
|
|
14013
14059
|
const aligned = this.alignFlamePair(from, to2);
|
|
14014
|
-
let keyframes =
|
|
14060
|
+
let keyframes = useLinearInterpolation ? generateTransitionFramesLinear(
|
|
14061
|
+
aligned.from,
|
|
14062
|
+
aligned.to,
|
|
14063
|
+
durationMs,
|
|
14064
|
+
APP_CONFIG.animation.fps
|
|
14065
|
+
) : generateTransitionFrames(
|
|
14015
14066
|
aligned.from,
|
|
14016
14067
|
aligned.to,
|
|
14017
14068
|
durationMs,
|
|
@@ -14378,6 +14429,7 @@ class AvatarView {
|
|
|
14378
14429
|
const steps = this.transitionKeyframes.length;
|
|
14379
14430
|
const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
|
|
14380
14431
|
const currentFrame = this.transitionKeyframes[idx];
|
|
14432
|
+
this.currentPlayingFrame = currentFrame;
|
|
14381
14433
|
const wasmParams = convertProtoFlameToWasmParams(currentFrame);
|
|
14382
14434
|
const avatarCore = AvatarSDK.getAvatarCore();
|
|
14383
14435
|
if (avatarCore) {
|
|
@@ -14450,6 +14502,7 @@ class AvatarView {
|
|
|
14450
14502
|
this.lastRenderedFrameIndex = frameIndex;
|
|
14451
14503
|
if (frameIndex >= 0 && frameIndex < this.currentKeyframes.length) {
|
|
14452
14504
|
this.lastRealtimeProtoFrame = this.currentKeyframes[frameIndex];
|
|
14505
|
+
this.currentPlayingFrame = this.lastRealtimeProtoFrame;
|
|
14453
14506
|
}
|
|
14454
14507
|
}
|
|
14455
14508
|
setState(newState) {
|
|
@@ -14525,12 +14578,31 @@ class AvatarView {
|
|
|
14525
14578
|
if (this.renderingState !== "transitioningToSpeaking") {
|
|
14526
14579
|
return;
|
|
14527
14580
|
}
|
|
14528
|
-
|
|
14529
|
-
|
|
14581
|
+
let fromFrame = this.currentPlayingFrame;
|
|
14582
|
+
if (!fromFrame) {
|
|
14583
|
+
if (state === "idle") {
|
|
14584
|
+
const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
|
|
14585
|
+
fromFrame = convertWasmParamsToProtoFlame(idleParams);
|
|
14586
|
+
} else if (state === "transitioningToIdle" || state === "transitioningToSpeaking") {
|
|
14587
|
+
if (this.transitionKeyframes.length > 0) {
|
|
14588
|
+
const elapsed = performance.now() - this.transitionStartTime;
|
|
14589
|
+
const currentTransitionDurationMs = state === "transitioningToSpeaking" ? this.startTransitionDurationMs : this.endTransitionDurationMs;
|
|
14590
|
+
const progress = Math.min(1, Math.max(0, elapsed / currentTransitionDurationMs));
|
|
14591
|
+
const steps = this.transitionKeyframes.length;
|
|
14592
|
+
const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
|
|
14593
|
+
fromFrame = this.transitionKeyframes[idx];
|
|
14594
|
+
}
|
|
14595
|
+
}
|
|
14596
|
+
}
|
|
14597
|
+
if (!fromFrame) {
|
|
14598
|
+
logger.warn("[AvatarView] Cannot get current playing frame, fallback to idle frame");
|
|
14599
|
+
const idleParams = await avatarCore.getCurrentFrameParams(this.idleCurrentFrameIndex, this.characterId);
|
|
14600
|
+
fromFrame = convertWasmParamsToProtoFlame(idleParams);
|
|
14601
|
+
}
|
|
14530
14602
|
await this.getCachedIdleFirstFrame();
|
|
14531
14603
|
const firstSpeaking = keyframes[0];
|
|
14532
14604
|
const firstSpeakingWithPostProcessing = this.avatarController.applyPostProcessingToFlame(firstSpeaking);
|
|
14533
|
-
this.transitionKeyframes = this.generateAndAlignTransitionFrames(
|
|
14605
|
+
this.transitionKeyframes = this.generateAndAlignTransitionFrames(fromFrame, firstSpeakingWithPostProcessing, this.startTransitionDurationMs, true);
|
|
14534
14606
|
this.transitionStartTime = performance.now();
|
|
14535
14607
|
if (this.transitionKeyframes.length === 0) {
|
|
14536
14608
|
this.setState("speaking");
|
|
@@ -14564,7 +14636,10 @@ class AvatarView {
|
|
|
14564
14636
|
if (state === "idle" || state === "transitioningToIdle") {
|
|
14565
14637
|
return;
|
|
14566
14638
|
}
|
|
14567
|
-
if (state !== "speaking") {
|
|
14639
|
+
if (state !== "speaking" && state !== "transitioningToSpeaking") {
|
|
14640
|
+
if (state === "transitioningToIdle") {
|
|
14641
|
+
return;
|
|
14642
|
+
}
|
|
14568
14643
|
this.setState("idle");
|
|
14569
14644
|
this.stopRealtimeAnimationLoop();
|
|
14570
14645
|
this.startIdleAnimationLoop();
|
|
@@ -14578,12 +14653,33 @@ class AvatarView {
|
|
|
14578
14653
|
return;
|
|
14579
14654
|
}
|
|
14580
14655
|
const avatarCore = AvatarSDK.getAvatarCore();
|
|
14581
|
-
if (avatarCore
|
|
14582
|
-
|
|
14583
|
-
|
|
14656
|
+
if (avatarCore) {
|
|
14657
|
+
let fromFrame = this.currentPlayingFrame;
|
|
14658
|
+
if (!fromFrame) {
|
|
14659
|
+
if (this.lastRealtimeProtoFrame) {
|
|
14660
|
+
fromFrame = this.lastRealtimeProtoFrame;
|
|
14661
|
+
} else if (this.currentKeyframes.length > 0) {
|
|
14662
|
+
fromFrame = this.currentKeyframes[Math.max(0, this.lastRenderedFrameIndex)];
|
|
14663
|
+
}
|
|
14664
|
+
}
|
|
14665
|
+
if (!fromFrame && this.transitionKeyframes.length > 0) {
|
|
14666
|
+
const elapsed = performance.now() - this.transitionStartTime;
|
|
14667
|
+
const progress = Math.min(1, Math.max(0, elapsed / this.endTransitionDurationMs));
|
|
14668
|
+
const steps = this.transitionKeyframes.length;
|
|
14669
|
+
const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
|
|
14670
|
+
fromFrame = this.transitionKeyframes[idx];
|
|
14671
|
+
}
|
|
14672
|
+
if (!fromFrame) {
|
|
14673
|
+
logger.warn("[AvatarView] Cannot get current playing frame for transition to idle, fallback to idle frame");
|
|
14674
|
+
this.setState("idle");
|
|
14675
|
+
this.stopRealtimeAnimationLoop();
|
|
14676
|
+
this.startIdleAnimationLoop();
|
|
14677
|
+
return;
|
|
14678
|
+
}
|
|
14679
|
+
const fromFrameWithPostProcessing = this.avatarController.applyPostProcessingToFlame(fromFrame);
|
|
14584
14680
|
const idleFirstProto = await this.getCachedIdleFirstFrame();
|
|
14585
14681
|
if (idleFirstProto) {
|
|
14586
|
-
this.transitionKeyframes = this.generateAndAlignTransitionFrames(
|
|
14682
|
+
this.transitionKeyframes = this.generateAndAlignTransitionFrames(fromFrameWithPostProcessing, idleFirstProto, this.endTransitionDurationMs);
|
|
14587
14683
|
this.transitionStartTime = performance.now();
|
|
14588
14684
|
if (this.transitionKeyframes.length > 0 && this.renderingState === "transitioningToIdle") {
|
|
14589
14685
|
if (APP_CONFIG.debug)
|
|
@@ -14621,6 +14717,7 @@ class AvatarView {
|
|
|
14621
14717
|
this.setState("idle");
|
|
14622
14718
|
this.cachedIdleFirstFrame = null;
|
|
14623
14719
|
this.idleCurrentFrameIndex = 0;
|
|
14720
|
+
this.currentPlayingFrame = null;
|
|
14624
14721
|
const avatarCore = AvatarSDK.getAvatarCore();
|
|
14625
14722
|
if (avatarCore && this.characterHandle) {
|
|
14626
14723
|
try {
|
|
@@ -14713,7 +14810,13 @@ class AvatarView {
|
|
|
14713
14810
|
const to2 = transitionType === "start" ? aligned.to : aligned.from;
|
|
14714
14811
|
const fps = APP_CONFIG.animation.fps;
|
|
14715
14812
|
const durationMs = frameCount / fps * 1e3;
|
|
14716
|
-
const
|
|
14813
|
+
const useLinear = transitionType === "start";
|
|
14814
|
+
const transitionFrames = useLinear ? generateTransitionFramesLinear(
|
|
14815
|
+
from,
|
|
14816
|
+
to2,
|
|
14817
|
+
durationMs,
|
|
14818
|
+
fps
|
|
14819
|
+
) : generateTransitionFrames(
|
|
14717
14820
|
from,
|
|
14718
14821
|
to2,
|
|
14719
14822
|
durationMs,
|
|
@@ -14796,7 +14899,6 @@ export {
|
|
|
14796
14899
|
Environment as E,
|
|
14797
14900
|
LogLevel as L,
|
|
14798
14901
|
ResourceType as R,
|
|
14799
|
-
SPAvatarError as S,
|
|
14800
14902
|
logEvent as a,
|
|
14801
14903
|
Avatar as b,
|
|
14802
14904
|
AvatarController as c,
|
|
@@ -14809,5 +14911,6 @@ export {
|
|
|
14809
14911
|
AvatarState as j,
|
|
14810
14912
|
ErrorCode as k,
|
|
14811
14913
|
logger as l,
|
|
14812
|
-
|
|
14914
|
+
AvatarError as m,
|
|
14915
|
+
extractResourceUrls as n
|
|
14813
14916
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { b, c, f, d, j, g, C, i, D, E, k, h, L, R,
|
|
1
|
+
import { b, c, m, f, d, j, g, C, i, D, E, k, h, L, R, n } from "./index-D0sXXlqd.js";
|
|
2
2
|
export {
|
|
3
3
|
b as Avatar,
|
|
4
4
|
c as AvatarController,
|
|
5
|
+
m as AvatarError,
|
|
5
6
|
f as AvatarManager,
|
|
6
7
|
d as AvatarSDK,
|
|
7
8
|
j as AvatarState,
|
|
@@ -14,6 +15,5 @@ export {
|
|
|
14
15
|
h as LoadProgress,
|
|
15
16
|
L as LogLevel,
|
|
16
17
|
R as ResourceType,
|
|
17
|
-
|
|
18
|
-
m as extractResourceUrls
|
|
18
|
+
n as extractResourceUrls
|
|
19
19
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -74,7 +74,7 @@ export declare enum ErrorCode {
|
|
|
74
74
|
|
|
75
75
|
failedToDownloadAvatarAssets = "failedToDownloadAvatarAssets"
|
|
76
76
|
}
|
|
77
|
-
export declare class
|
|
77
|
+
export declare class AvatarError extends Error {
|
|
78
78
|
code?: (string | ErrorCode) | undefined;
|
|
79
79
|
constructor(message: string, code?: (string | ErrorCode) | undefined);
|
|
80
80
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { Flame } from '../generated/driveningress/v1/driveningress';
|
|
2
2
|
|
|
3
|
+
export declare function linearLerp(from: Flame, to: Flame, progress: number): Flame;
|
|
4
|
+
|
|
5
|
+
export declare function generateTransitionFramesLinear(from: Flame, to: Flame, durationMs: number, fps?: number): Flame[];
|
|
6
|
+
|
|
3
7
|
export declare function bezierLerp(from: Flame, to: Flame, progress: number): Flame;
|
|
4
8
|
|
|
5
9
|
export declare function generateTransitionFrames(from: Flame, to: Flame, durationMs: number, fps?: number): Flame[];
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default:
|
|
1
|
+
declare const _default: any;
|
|
2
2
|
export default _default;
|
package/dist/vite.d.ts
ADDED