talking-head-studio 0.4.9 → 0.4.11
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/README.md +227 -351
- package/dist/TalkingHead.d.ts +16 -25
- package/dist/TalkingHead.web.d.ts +6 -0
- package/dist/TalkingHead.web.js +18 -8
- package/dist/api/studioApi.js +25 -26
- package/dist/appearance/apply.js +2 -3
- package/dist/appearance/matchers.js +1 -2
- package/dist/appearance/schema.js +1 -2
- package/dist/core/avatar/backend.d.ts +130 -0
- package/dist/core/avatar/backend.js +4 -0
- package/dist/core/avatar/backends/gaussian.d.ts +49 -0
- package/dist/core/avatar/backends/gaussian.js +291 -0
- package/dist/core/avatar/backends/index.d.ts +3 -0
- package/dist/core/avatar/backends/index.js +7 -0
- package/dist/core/avatar/backends/morphTarget.d.ts +39 -0
- package/dist/core/avatar/backends/morphTarget.js +179 -0
- package/dist/core/avatar/faceControls.d.ts +40 -0
- package/dist/core/avatar/faceControls.js +138 -0
- package/dist/core/avatar/schema.d.ts +50 -0
- package/dist/core/avatar/schema.js +134 -0
- package/dist/core/avatar/visemes.d.ts +64 -0
- package/dist/core/avatar/visemes.js +72 -0
- package/dist/editor/AvatarCanvas.js +1 -2
- package/dist/editor/AvatarEditor.native.js +18 -9
- package/dist/editor/AvatarModel.js +1 -2
- package/dist/editor/FaceSqueezeEditor.js +19 -9
- package/dist/editor/FaceSqueezeEditor.web.js +2 -2
- package/dist/editor/RigidAccessory.js +1 -2
- package/dist/editor/SkinnedClothing.js +18 -9
- package/dist/editor/boneSnap.js +22 -12
- package/dist/editor/studioTheme.js +2 -2
- package/dist/html.js +1 -2
- package/dist/index.d.ts +15 -1
- package/dist/index.js +28 -5
- package/dist/platform/api/types.d.ts +10 -0
- package/dist/platform/api/types.js +2 -0
- package/dist/platform/marketplace/types.d.ts +32 -0
- package/dist/platform/marketplace/types.js +2 -0
- package/dist/platform/sdk/unity.d.ts +27 -0
- package/dist/platform/sdk/unity.js +2 -0
- package/dist/platform/sdk/unreal.d.ts +23 -0
- package/dist/platform/sdk/unreal.js +2 -0
- package/dist/platform/sdk/web.d.ts +16 -0
- package/dist/platform/sdk/web.js +2 -0
- package/dist/sketchfab/api.js +4 -5
- package/dist/sketchfab/useSketchfabSearch.js +1 -2
- package/dist/tts/useDirectVisemeStream.d.ts +2 -6
- package/dist/tts/useDirectVisemeStream.js +1 -2
- package/dist/tts/useMotionMarkers.d.ts +0 -1
- package/dist/tts/useMotionMarkers.js +1 -2
- package/dist/utils/avatarUtils.js +2 -3
- package/dist/utils/faceLandmarkerToShapeWeights.js +19 -10
- package/dist/voice/convertToWav.js +1 -2
- package/dist/voice/index.d.ts +3 -0
- package/dist/voice/index.js +6 -1
- package/dist/voice/useAudioPlayer.js +1 -2
- package/dist/voice/useAudioRecording.js +1 -2
- package/dist/voice/useFaceControls.d.ts +14 -0
- package/dist/voice/useFaceControls.js +81 -0
- package/dist/voice/useVoicePreview.d.ts +7 -0
- package/dist/voice/useVoicePreview.js +81 -0
- package/dist/wardrobe/index.d.ts +2 -0
- package/dist/wardrobe/index.js +3 -1
- package/dist/wardrobe/useAvatarWardrobeHydration.js +1 -2
- package/dist/wardrobe/useStudioAvatar.d.ts +29 -0
- package/dist/wardrobe/useStudioAvatar.js +177 -0
- package/dist/wgpu/WgpuAvatar.js +17 -7
- package/dist/wgpu/useAuthedModelUri.js +18 -9
- package/package.json +8 -4
package/dist/TalkingHead.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
export type TalkingHeadMood = 'neutral' | 'happy' | 'sad' | 'angry' | 'excited' | 'thinking' | 'concerned' | 'surprised';
|
|
4
|
+
import type { AgentVisemePayload, OculusViseme, VisemeCue } from './core/avatar/visemes';
|
|
4
5
|
export type TalkingHeadLoadingStage = 'booting' | 'fetching_model' | 'loading_avatar' | 'loading_fallback' | 'ready';
|
|
5
6
|
export interface TalkingHeadLoadingState {
|
|
6
7
|
stage: TalkingHeadLoadingStage;
|
|
@@ -9,36 +10,20 @@ export interface TalkingHeadLoadingState {
|
|
|
9
10
|
/**
|
|
10
11
|
* Standard viseme keys supported by the avatar.
|
|
11
12
|
* Use with sendViseme() from your TTS viseme callbacks.
|
|
13
|
+
* @deprecated Use OculusViseme from @yourplatform/avatar/core/avatar/visemes
|
|
12
14
|
*/
|
|
13
|
-
export type TalkingHeadViseme =
|
|
14
|
-
/**
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
15
|
+
export type TalkingHeadViseme = OculusViseme;
|
|
16
|
+
/**
|
|
17
|
+
* Rhubarb mouth shape cue (Preston Blair set: A-H, X)
|
|
18
|
+
* @deprecated Use VisemeCue from @yourplatform/avatar/core/avatar/visemes
|
|
19
|
+
*/
|
|
20
|
+
export type TalkingHeadVisemeCue = VisemeCue;
|
|
20
21
|
/**
|
|
21
22
|
* A full viseme schedule from the Rhubarb sidecar endpoint.
|
|
22
23
|
* Pass to scheduleVisemes() when agent_visemes arrives on the data channel.
|
|
24
|
+
* @deprecated Use AgentVisemePayload from @yourplatform/avatar/core/avatar/visemes
|
|
23
25
|
*/
|
|
24
|
-
export
|
|
25
|
-
/** Matches X-TTS-Request-Id / agent_visemes.requestId */
|
|
26
|
-
requestId?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Wall-clock ms at which the TTS request was fired (agent side).
|
|
29
|
-
* Used as the scheduling anchor plus AUDIO_PIPELINE_DELAY_MS.
|
|
30
|
-
*/
|
|
31
|
-
startedAtMs?: number;
|
|
32
|
-
/**
|
|
33
|
-
* Wall-clock ms at which audio actually began playing in the speaker.
|
|
34
|
-
* When present, used directly as the scheduling anchor with no additional
|
|
35
|
-
* pipeline offset — more accurate than startedAtMs on fast connections.
|
|
36
|
-
* Stamp this from the LiveKit onAudioPlaybackStarted callback if available.
|
|
37
|
-
*/
|
|
38
|
-
audioStartedAtMs?: number;
|
|
39
|
-
durationMs?: number;
|
|
40
|
-
cues: TalkingHeadVisemeCue[];
|
|
41
|
-
}
|
|
26
|
+
export type TalkingHeadVisemeSchedule = AgentVisemePayload;
|
|
42
27
|
export interface TalkingHeadAccessory {
|
|
43
28
|
id: string;
|
|
44
29
|
url: string;
|
|
@@ -66,6 +51,9 @@ export interface TalkingHeadProps {
|
|
|
66
51
|
/** Base URL for vendored assets. When set, replaces all cdn.jsdelivr.net references. */
|
|
67
52
|
vendorBaseUrl?: string | null;
|
|
68
53
|
}
|
|
54
|
+
/** @deprecated Use AvatarPlayerProps */
|
|
55
|
+
export type TalkingHeadPropsAlias = TalkingHeadProps;
|
|
56
|
+
export type AvatarPlayerProps = TalkingHeadProps;
|
|
69
57
|
export interface TalkingHeadRef {
|
|
70
58
|
sendAmplitude: (amplitude: number) => void;
|
|
71
59
|
/**
|
|
@@ -103,4 +91,7 @@ export interface TalkingHeadRef {
|
|
|
103
91
|
/** Dispatch a named motion/gesture to the avatar (e.g. 'wave_right', 'dance_idle'). */
|
|
104
92
|
dispatchMotion: (name: string) => void;
|
|
105
93
|
}
|
|
94
|
+
/** @deprecated Use AvatarPlayerRef */
|
|
95
|
+
export type TalkingHeadRefAlias = TalkingHeadRef;
|
|
96
|
+
export type AvatarPlayerRef = TalkingHeadRef;
|
|
106
97
|
export declare const TalkingHead: React.ForwardRefExoticComponent<TalkingHeadProps & React.RefAttributes<TalkingHeadRef>>;
|
|
@@ -26,6 +26,9 @@ export interface TalkingHeadProps {
|
|
|
26
26
|
onAvatarState?: (state: string) => void;
|
|
27
27
|
style?: React.CSSProperties;
|
|
28
28
|
}
|
|
29
|
+
/** @deprecated Use AvatarPlayerProps */
|
|
30
|
+
export type TalkingHeadPropsAlias = TalkingHeadProps;
|
|
31
|
+
export type AvatarPlayerProps = TalkingHeadProps;
|
|
29
32
|
export interface TalkingHeadRef {
|
|
30
33
|
sendAmplitude: (amplitude: number) => void;
|
|
31
34
|
scheduleVisemes: (schedule: TalkingHeadVisemeSchedule) => void;
|
|
@@ -37,4 +40,7 @@ export interface TalkingHeadRef {
|
|
|
37
40
|
setAccessories: (accessories: TalkingHeadAccessory[]) => void;
|
|
38
41
|
dispatchMotion: (name: string) => void;
|
|
39
42
|
}
|
|
43
|
+
/** @deprecated Use AvatarPlayerRef */
|
|
44
|
+
export type TalkingHeadRefAlias = TalkingHeadRef;
|
|
45
|
+
export type AvatarPlayerRef = TalkingHeadRef;
|
|
40
46
|
export declare const TalkingHead: React.ForwardRefExoticComponent<TalkingHeadProps & React.RefAttributes<TalkingHeadRef>>;
|
package/dist/TalkingHead.web.js
CHANGED
|
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
36
|
exports.TalkingHead = void 0;
|
|
27
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
@@ -203,6 +213,6 @@ exports.TalkingHead = (0, react_1.forwardRef)(({ avatarUrl, authToken, mood = 'n
|
|
|
203
213
|
window.addEventListener('message', onMessage);
|
|
204
214
|
return () => window.removeEventListener('message', onMessage);
|
|
205
215
|
}, [post]);
|
|
206
|
-
return ((0, jsx_runtime_1.jsx)("div", { style: { ...containerStyle, ...style }, children: (0, jsx_runtime_1.jsx)("iframe", { ref: iframeRef, srcDoc: srcdoc, style: iframeStyle,
|
|
216
|
+
return ((0, jsx_runtime_1.jsx)("div", { style: { ...containerStyle, ...style }, children: (0, jsx_runtime_1.jsx)("iframe", { ref: iframeRef, srcDoc: srcdoc, style: iframeStyle, title: "TalkingHead Avatar" }) }));
|
|
207
217
|
});
|
|
208
218
|
exports.TalkingHead.displayName = 'TalkingHead';
|
package/dist/api/studioApi.js
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.configureAvatarApi = configureAvatarApi;
|
|
4
|
+
exports.getToken = getToken;
|
|
5
|
+
exports.studioFetch = studioFetch;
|
|
6
|
+
exports.getMyAvatars = getMyAvatars;
|
|
7
|
+
exports.getAvatar = getAvatar;
|
|
8
|
+
exports.updateAvatar = updateAvatar;
|
|
9
|
+
exports.deleteAvatar = deleteAvatar;
|
|
10
|
+
exports.createAvatar = createAvatar;
|
|
11
|
+
exports.getPublicAvatars = getPublicAvatars;
|
|
12
|
+
exports.getVoiceProfileSamples = getVoiceProfileSamples;
|
|
13
|
+
exports.getVoiceProfiles = getVoiceProfiles;
|
|
14
|
+
exports.setDefaultVoice = setDefaultVoice;
|
|
15
|
+
exports.removeDefaultVoice = removeDefaultVoice;
|
|
16
|
+
exports.avatarFileUrl = avatarFileUrl;
|
|
17
|
+
exports.avatarThumbnailUrl = avatarThumbnailUrl;
|
|
18
|
+
exports.avatarAnimatedThumbnailUrl = avatarAnimatedThumbnailUrl;
|
|
19
|
+
exports.thumbnailHeaders = thumbnailHeaders;
|
|
20
|
+
exports.listAssets = listAssets;
|
|
21
|
+
exports.getAsset = getAsset;
|
|
22
|
+
exports.uploadAsset = uploadAsset;
|
|
23
|
+
exports.deleteAsset = deleteAsset;
|
|
24
|
+
exports.suggestPlacement = suggestPlacement;
|
|
25
|
+
exports.assetFileUrl = assetFileUrl;
|
|
26
|
+
exports.createVoiceProfile = createVoiceProfile;
|
|
27
|
+
exports.uploadVoiceSample = uploadVoiceSample;
|
|
4
28
|
// ---------------------------------------------------------------------------
|
|
5
29
|
// Injectable configuration
|
|
6
30
|
// ---------------------------------------------------------------------------
|
|
@@ -11,11 +35,9 @@ function configureAvatarApi(opts) {
|
|
|
11
35
|
_baseUrl = opts.baseUrl;
|
|
12
36
|
_getToken = opts.getToken;
|
|
13
37
|
}
|
|
14
|
-
exports.configureAvatarApi = configureAvatarApi;
|
|
15
38
|
async function getToken() {
|
|
16
39
|
return _getToken ? _getToken() : null;
|
|
17
40
|
}
|
|
18
|
-
exports.getToken = getToken;
|
|
19
41
|
// ---------------------------------------------------------------------------
|
|
20
42
|
// Core fetch wrappers
|
|
21
43
|
// ---------------------------------------------------------------------------
|
|
@@ -48,7 +70,6 @@ async function studioFetch(path, options) {
|
|
|
48
70
|
}
|
|
49
71
|
return res.json();
|
|
50
72
|
}
|
|
51
|
-
exports.studioFetch = studioFetch;
|
|
52
73
|
/** POST/PUT with FormData, sharing auth token logic. */
|
|
53
74
|
async function studioFetchForm(path, method, formData) {
|
|
54
75
|
const token = await getToken();
|
|
@@ -76,11 +97,9 @@ async function studioFetchForm(path, method, formData) {
|
|
|
76
97
|
function getMyAvatars() {
|
|
77
98
|
return studioFetch('/v1/avatars');
|
|
78
99
|
}
|
|
79
|
-
exports.getMyAvatars = getMyAvatars;
|
|
80
100
|
function getAvatar(id) {
|
|
81
101
|
return studioFetch(`/v1/avatars/${id}`);
|
|
82
102
|
}
|
|
83
|
-
exports.getAvatar = getAvatar;
|
|
84
103
|
function updateAvatar(id, data) {
|
|
85
104
|
return studioFetch(`/v1/avatars/${id}`, {
|
|
86
105
|
method: 'PATCH',
|
|
@@ -88,11 +107,9 @@ function updateAvatar(id, data) {
|
|
|
88
107
|
body: JSON.stringify(data),
|
|
89
108
|
});
|
|
90
109
|
}
|
|
91
|
-
exports.updateAvatar = updateAvatar;
|
|
92
110
|
function deleteAvatar(id) {
|
|
93
111
|
return studioFetch(`/v1/avatars/${id}`, { method: 'DELETE' });
|
|
94
112
|
}
|
|
95
|
-
exports.deleteAvatar = deleteAvatar;
|
|
96
113
|
async function createAvatar(fileUri, name, description) {
|
|
97
114
|
const formData = new FormData();
|
|
98
115
|
formData.append('file', {
|
|
@@ -106,22 +123,18 @@ async function createAvatar(fileUri, name, description) {
|
|
|
106
123
|
}
|
|
107
124
|
return studioFetchForm('/v1/avatars', 'POST', formData);
|
|
108
125
|
}
|
|
109
|
-
exports.createAvatar = createAvatar;
|
|
110
126
|
function getPublicAvatars() {
|
|
111
127
|
return studioFetch('/v1/avatars/public');
|
|
112
128
|
}
|
|
113
|
-
exports.getPublicAvatars = getPublicAvatars;
|
|
114
129
|
// ---------------------------------------------------------------------------
|
|
115
130
|
// Voice profile endpoints
|
|
116
131
|
// ---------------------------------------------------------------------------
|
|
117
132
|
function getVoiceProfileSamples(profileId) {
|
|
118
133
|
return studioFetch(`/profiles/${profileId}/samples`);
|
|
119
134
|
}
|
|
120
|
-
exports.getVoiceProfileSamples = getVoiceProfileSamples;
|
|
121
135
|
function getVoiceProfiles() {
|
|
122
136
|
return studioFetch('/profiles');
|
|
123
137
|
}
|
|
124
|
-
exports.getVoiceProfiles = getVoiceProfiles;
|
|
125
138
|
function setDefaultVoice(avatarId, profileId) {
|
|
126
139
|
return studioFetch(`/v1/avatars/${avatarId}/default-voice`, {
|
|
127
140
|
method: 'PUT',
|
|
@@ -129,13 +142,11 @@ function setDefaultVoice(avatarId, profileId) {
|
|
|
129
142
|
body: JSON.stringify({ profile_id: profileId }),
|
|
130
143
|
});
|
|
131
144
|
}
|
|
132
|
-
exports.setDefaultVoice = setDefaultVoice;
|
|
133
145
|
function removeDefaultVoice(avatarId) {
|
|
134
146
|
return studioFetch(`/v1/avatars/${avatarId}/default-voice`, {
|
|
135
147
|
method: 'DELETE',
|
|
136
148
|
});
|
|
137
149
|
}
|
|
138
|
-
exports.removeDefaultVoice = removeDefaultVoice;
|
|
139
150
|
// ---------------------------------------------------------------------------
|
|
140
151
|
// URL helpers
|
|
141
152
|
// ---------------------------------------------------------------------------
|
|
@@ -143,27 +154,23 @@ function avatarFileUrl(avatar) {
|
|
|
143
154
|
const base = `${_baseUrl}${avatar.url}`;
|
|
144
155
|
return avatar.updated_at ? `${base}?v=${encodeURIComponent(avatar.updated_at)}` : base;
|
|
145
156
|
}
|
|
146
|
-
exports.avatarFileUrl = avatarFileUrl;
|
|
147
157
|
function avatarThumbnailUrl(avatar) {
|
|
148
158
|
const url = avatar.thumbnail_url || avatar.animated_thumbnail_url;
|
|
149
159
|
if (!url)
|
|
150
160
|
return null;
|
|
151
161
|
return `${_baseUrl}${url}`;
|
|
152
162
|
}
|
|
153
|
-
exports.avatarThumbnailUrl = avatarThumbnailUrl;
|
|
154
163
|
function avatarAnimatedThumbnailUrl(avatar) {
|
|
155
164
|
if (!avatar.animated_thumbnail_url)
|
|
156
165
|
return null;
|
|
157
166
|
return `${_baseUrl}${avatar.animated_thumbnail_url}`;
|
|
158
167
|
}
|
|
159
|
-
exports.avatarAnimatedThumbnailUrl = avatarAnimatedThumbnailUrl;
|
|
160
168
|
async function thumbnailHeaders() {
|
|
161
169
|
const token = await getToken();
|
|
162
170
|
if (!token)
|
|
163
171
|
return {};
|
|
164
172
|
return { Authorization: `Bearer ${token}` };
|
|
165
173
|
}
|
|
166
|
-
exports.thumbnailHeaders = thumbnailHeaders;
|
|
167
174
|
// ---------------------------------------------------------------------------
|
|
168
175
|
// Wearable Asset endpoints
|
|
169
176
|
// ---------------------------------------------------------------------------
|
|
@@ -171,11 +178,9 @@ function listAssets(category) {
|
|
|
171
178
|
const query = category ? `?category=${encodeURIComponent(category)}` : '';
|
|
172
179
|
return studioFetch(`/v1/assets${query}`);
|
|
173
180
|
}
|
|
174
|
-
exports.listAssets = listAssets;
|
|
175
181
|
function getAsset(id) {
|
|
176
182
|
return studioFetch(`/v1/assets/${id}`);
|
|
177
183
|
}
|
|
178
|
-
exports.getAsset = getAsset;
|
|
179
184
|
async function uploadAsset(fileUri, meta) {
|
|
180
185
|
const formData = new FormData();
|
|
181
186
|
formData.append('file', {
|
|
@@ -198,20 +203,16 @@ async function uploadAsset(fileUri, meta) {
|
|
|
198
203
|
formData.append('hides_body_parts', JSON.stringify(meta.hides_body_parts));
|
|
199
204
|
return studioFetchForm('/v1/assets', 'POST', formData);
|
|
200
205
|
}
|
|
201
|
-
exports.uploadAsset = uploadAsset;
|
|
202
206
|
function deleteAsset(id) {
|
|
203
207
|
return studioFetch(`/v1/assets/${id}`, { method: 'DELETE' });
|
|
204
208
|
}
|
|
205
|
-
exports.deleteAsset = deleteAsset;
|
|
206
209
|
function suggestPlacement(assetId, avatarId) {
|
|
207
210
|
return studioFetch(`/v1/assets/${assetId}/suggest-placement?avatar_id=${encodeURIComponent(avatarId)}`, { method: 'POST' });
|
|
208
211
|
}
|
|
209
|
-
exports.suggestPlacement = suggestPlacement;
|
|
210
212
|
function assetFileUrl(asset) {
|
|
211
213
|
const base = `${_baseUrl}${asset.url}`;
|
|
212
214
|
return asset.updated_at ? `${base}?v=${encodeURIComponent(asset.updated_at)}` : base;
|
|
213
215
|
}
|
|
214
|
-
exports.assetFileUrl = assetFileUrl;
|
|
215
216
|
async function createVoiceProfile(name, language = 'en') {
|
|
216
217
|
return studioFetch('/profiles', {
|
|
217
218
|
method: 'POST',
|
|
@@ -221,7 +222,6 @@ async function createVoiceProfile(name, language = 'en') {
|
|
|
221
222
|
body: JSON.stringify({ name, language }),
|
|
222
223
|
});
|
|
223
224
|
}
|
|
224
|
-
exports.createVoiceProfile = createVoiceProfile;
|
|
225
225
|
async function uploadVoiceSample(profileId, fileUri, fileName, referenceText) {
|
|
226
226
|
const formData = new FormData();
|
|
227
227
|
formData.append('reference_text', referenceText);
|
|
@@ -233,4 +233,3 @@ async function uploadVoiceSample(profileId, fileUri, fileName, referenceText) {
|
|
|
233
233
|
});
|
|
234
234
|
return studioFetchForm(`/profiles/${profileId}/samples`, 'POST', formData);
|
|
235
235
|
}
|
|
236
|
-
exports.uploadVoiceSample = uploadVoiceSample;
|
package/dist/appearance/apply.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.invalidateMorphCache = invalidateMorphCache;
|
|
4
|
+
exports.applyAppearanceToObject3D = applyAppearanceToObject3D;
|
|
4
5
|
const matchers_1 = require("./matchers");
|
|
5
6
|
const schema_1 = require("./schema");
|
|
6
7
|
function asMaterialArray(material) {
|
|
@@ -60,7 +61,6 @@ function getMorphCache(object3d) {
|
|
|
60
61
|
function invalidateMorphCache(object3d) {
|
|
61
62
|
morphCacheByScene.delete(object3d);
|
|
62
63
|
}
|
|
63
|
-
exports.invalidateMorphCache = invalidateMorphCache;
|
|
64
64
|
// ---------------------------------------------------------------------------
|
|
65
65
|
// Main apply function
|
|
66
66
|
// ---------------------------------------------------------------------------
|
|
@@ -121,4 +121,3 @@ function applyAppearanceToObject3D(object3d, appearance) {
|
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
-
exports.applyAppearanceToObject3D = applyAppearanceToObject3D;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.pickTargetForMaterialName =
|
|
3
|
+
exports.pickTargetForMaterialName = pickTargetForMaterialName;
|
|
4
4
|
function tokenizeMaterialName(name) {
|
|
5
5
|
return name
|
|
6
6
|
.replace(/([a-z\d])([A-Z])/g, '$1 $2')
|
|
@@ -33,4 +33,3 @@ function pickTargetForMaterialName(name) {
|
|
|
33
33
|
}
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
36
|
-
exports.pickTargetForMaterialName = pickTargetForMaterialName;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.normalizeAppearance =
|
|
3
|
+
exports.normalizeAppearance = normalizeAppearance;
|
|
4
4
|
const HEX_COLOR_PATTERN = /^#(?:[\dA-Fa-f]{3}|[\dA-Fa-f]{6})$/;
|
|
5
5
|
function normalizeHexColor(value) {
|
|
6
6
|
if (!HEX_COLOR_PATTERN.test(value)) {
|
|
@@ -22,4 +22,3 @@ function normalizeAppearance(input) {
|
|
|
22
22
|
faceShapeWeights: input.faceShapeWeights,
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
|
-
exports.normalizeAppearance = normalizeAppearance;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { FaceControl } from './faceControls';
|
|
2
|
+
/**
|
|
3
|
+
* A render target is intentionally abstract here; concrete backends can
|
|
4
|
+
* accept whatever they need (WebGL/WebGPU context, React Native surface,
|
|
5
|
+
* WebView, etc.) via their own config layer.
|
|
6
|
+
*/
|
|
7
|
+
export type AvatarRenderTarget = unknown;
|
|
8
|
+
/**
|
|
9
|
+
* Canonical interface that all avatar renderers implement.
|
|
10
|
+
*
|
|
11
|
+
* The idea is:
|
|
12
|
+
* - talking-head-studio produces a stream of FaceControl values
|
|
13
|
+
* (pose + ExpressionState) from TTS visemes, emotions, and motions.
|
|
14
|
+
* - Each AvatarBackend implementation adapts those controls to its
|
|
15
|
+
* own rendering mechanism (morph targets, FLAME coefficients,
|
|
16
|
+
* Gaussian splats, 2D sprites, etc.).
|
|
17
|
+
*/
|
|
18
|
+
export interface AvatarBackend {
|
|
19
|
+
/** Optional one-time initialization step (e.g. load meshes, shaders, splats). */
|
|
20
|
+
initialize?(): Promise<void> | void;
|
|
21
|
+
/** Configure the backend with a render target (canvas, surface, WebView, etc.). */
|
|
22
|
+
attach(target: AvatarRenderTarget): Promise<void> | void;
|
|
23
|
+
/**
|
|
24
|
+
* Update the current pose + expression state.
|
|
25
|
+
* This should be a cheap, per-frame call.
|
|
26
|
+
*/
|
|
27
|
+
setControl(control: FaceControl): void;
|
|
28
|
+
/** Render the current control state into the attached target. */
|
|
29
|
+
renderFrame(): void;
|
|
30
|
+
/** Clean up graphics resources when the avatar is no longer needed. */
|
|
31
|
+
dispose?(): void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Per-avatar calibration profile, allowing each asset to respond to the
|
|
35
|
+
* canonical control space in its own way.
|
|
36
|
+
*
|
|
37
|
+
* This is intentionally lightweight and focused on scalar ranges and
|
|
38
|
+
* asymmetries instead of model-specific knobs. Backends can use these
|
|
39
|
+
* values to remap 0..1 sliders into whatever ranges their rigs expect.
|
|
40
|
+
*/
|
|
41
|
+
export type CalibrationProfile = {
|
|
42
|
+
/** Neutral pose + expression for this avatar. */
|
|
43
|
+
neutral: FaceControl;
|
|
44
|
+
/** Per-channel scalar ranges (0..1 logical → model space). */
|
|
45
|
+
ranges?: {
|
|
46
|
+
jawOpen?: {
|
|
47
|
+
min: number;
|
|
48
|
+
max: number;
|
|
49
|
+
};
|
|
50
|
+
mouthSmile?: {
|
|
51
|
+
min: number;
|
|
52
|
+
max: number;
|
|
53
|
+
};
|
|
54
|
+
mouthFunnel?: {
|
|
55
|
+
min: number;
|
|
56
|
+
max: number;
|
|
57
|
+
};
|
|
58
|
+
mouthPucker?: {
|
|
59
|
+
min: number;
|
|
60
|
+
max: number;
|
|
61
|
+
};
|
|
62
|
+
mouthWide?: {
|
|
63
|
+
min: number;
|
|
64
|
+
max: number;
|
|
65
|
+
};
|
|
66
|
+
upperLipRaise?: {
|
|
67
|
+
min: number;
|
|
68
|
+
max: number;
|
|
69
|
+
};
|
|
70
|
+
lowerLipDepress?: {
|
|
71
|
+
min: number;
|
|
72
|
+
max: number;
|
|
73
|
+
};
|
|
74
|
+
cheekRaise?: {
|
|
75
|
+
min: number;
|
|
76
|
+
max: number;
|
|
77
|
+
};
|
|
78
|
+
blinkLeft?: {
|
|
79
|
+
min: number;
|
|
80
|
+
max: number;
|
|
81
|
+
};
|
|
82
|
+
blinkRight?: {
|
|
83
|
+
min: number;
|
|
84
|
+
max: number;
|
|
85
|
+
};
|
|
86
|
+
browInnerUp?: {
|
|
87
|
+
min: number;
|
|
88
|
+
max: number;
|
|
89
|
+
};
|
|
90
|
+
browDownLeft?: {
|
|
91
|
+
min: number;
|
|
92
|
+
max: number;
|
|
93
|
+
};
|
|
94
|
+
browDownRight?: {
|
|
95
|
+
min: number;
|
|
96
|
+
max: number;
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
/** Simple asymmetry controls (e.g. left/right smile strength). */
|
|
100
|
+
asymmetry?: {
|
|
101
|
+
smileLeftBias?: number;
|
|
102
|
+
blinkLeftBias?: number;
|
|
103
|
+
};
|
|
104
|
+
/** Eye gaze range limits (default: full -1..1 on both axes). */
|
|
105
|
+
gazeLimits?: {
|
|
106
|
+
x?: {
|
|
107
|
+
min: number;
|
|
108
|
+
max: number;
|
|
109
|
+
};
|
|
110
|
+
y?: {
|
|
111
|
+
min: number;
|
|
112
|
+
max: number;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
/** Optional head pose limits to keep motion comfortable. */
|
|
116
|
+
poseLimits?: {
|
|
117
|
+
yaw?: {
|
|
118
|
+
min: number;
|
|
119
|
+
max: number;
|
|
120
|
+
};
|
|
121
|
+
pitch?: {
|
|
122
|
+
min: number;
|
|
123
|
+
max: number;
|
|
124
|
+
};
|
|
125
|
+
roll?: {
|
|
126
|
+
min: number;
|
|
127
|
+
max: number;
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type * as THREE from 'three';
|
|
2
|
+
import type { AvatarBackend, AvatarRenderTarget, CalibrationProfile } from '../backend';
|
|
3
|
+
import type { FaceControl } from '../faceControls';
|
|
4
|
+
import type { AvatarSchemaReport } from '../schema';
|
|
5
|
+
export interface GaussianBackendConfig {
|
|
6
|
+
/** Three.js scene root (gltf.scene). */
|
|
7
|
+
scene: THREE.Object3D;
|
|
8
|
+
/** Schema report from walkAvatarSchema — used to log coverage and skip morphs. */
|
|
9
|
+
schemaReport: AvatarSchemaReport;
|
|
10
|
+
/** Optional calibration profile for gaze limits and expression scaling. */
|
|
11
|
+
calibration?: CalibrationProfile;
|
|
12
|
+
/**
|
|
13
|
+
* 'fast' — only jaw bone driven (best for real-time on low-end devices).
|
|
14
|
+
* 'full' — all FLAME bone coefficients applied (default).
|
|
15
|
+
*/
|
|
16
|
+
patchQuality?: 'fast' | 'full';
|
|
17
|
+
}
|
|
18
|
+
export declare class GaussianBackend implements AvatarBackend {
|
|
19
|
+
private scene;
|
|
20
|
+
private schemaReport;
|
|
21
|
+
private calibration;
|
|
22
|
+
private patchQuality;
|
|
23
|
+
/** Resolved skeleton bone map: canonical name → Three.js Bone */
|
|
24
|
+
private boneMap;
|
|
25
|
+
/** Pre-computed deformation patches, one per Oculus viseme. */
|
|
26
|
+
private patches;
|
|
27
|
+
/** Snapshot of bone rotations at attach() — restored on dispose(). */
|
|
28
|
+
private basePose;
|
|
29
|
+
/** Last control applied — used in renderFrame() to avoid redundant writes. */
|
|
30
|
+
private lastControl;
|
|
31
|
+
constructor(config: GaussianBackendConfig);
|
|
32
|
+
initialize(): void;
|
|
33
|
+
attach(_target: AvatarRenderTarget): void;
|
|
34
|
+
setControl(control: FaceControl): void;
|
|
35
|
+
renderFrame(): void;
|
|
36
|
+
dispose(): void;
|
|
37
|
+
private _buildBoneMap;
|
|
38
|
+
private _captureBasePose;
|
|
39
|
+
private _restoreBasePose;
|
|
40
|
+
private _buildPatches;
|
|
41
|
+
private _applyControl;
|
|
42
|
+
private _applyGaze;
|
|
43
|
+
/** Canonical bones resolved from the skeleton. Useful for debugging rig coverage. */
|
|
44
|
+
get resolvedBones(): string[];
|
|
45
|
+
/** Number of viseme patches with at least one resolved bone. */
|
|
46
|
+
get patchCoverage(): number;
|
|
47
|
+
/** The schema report this backend was initialized with. */
|
|
48
|
+
get schema(): AvatarSchemaReport;
|
|
49
|
+
}
|