@voqalize/avatar 0.3.0 → 0.4.0
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/LICENSE-CC-BY-4.0 +396 -0
- package/README.md +65 -17
- package/assets/README.md +48 -0
- package/assets/tanya.glb +0 -0
- package/assets/tara.glb +0 -0
- package/assets/tushar.glb +0 -0
- package/client/AvatarClient.ts +337 -42
- package/client/arjun.ts +2 -0
- package/client/createAvatar.ts +38 -0
- package/client/index.ts +6 -1
- package/client/internal.ts +31 -3
- package/client/ishita.ts +2 -0
- package/client/kabir.ts +2 -0
- package/client/meera.ts +2 -0
- package/client/naina.ts +2 -0
- package/client/playout.ts +95 -0
- package/client/supports.ts +28 -0
- package/client/three/assets.ts +24 -0
- package/client/three/budgets.ts +27 -0
- package/client/three/holds.ts +64 -0
- package/client/three/internal.ts +54 -0
- package/client/three/motion-limits.json +116 -0
- package/client/three/sequences.ts +281 -0
- package/client/three/tanya.ts +69 -0
- package/client/three/tara-rig.ts +1562 -0
- package/client/three/tara.ts +76 -0
- package/client/three/tushar.ts +65 -0
- package/client/types.ts +109 -20
- package/client/vikram.ts +2 -0
- package/dist/AvatarClient.d.ts +89 -11
- package/dist/AvatarClient.d.ts.map +1 -1
- package/dist/AvatarClient.js +297 -35
- package/dist/AvatarClient.js.map +1 -1
- package/dist/arjun.d.ts +1 -0
- package/dist/arjun.d.ts.map +1 -1
- package/dist/arjun.js +2 -0
- package/dist/arjun.js.map +1 -1
- package/dist/createAvatar.d.ts +36 -0
- package/dist/createAvatar.d.ts.map +1 -1
- package/dist/createAvatar.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +9 -5
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +13 -5
- package/dist/internal.js.map +1 -1
- package/dist/ishita.d.ts +1 -0
- package/dist/ishita.d.ts.map +1 -1
- package/dist/ishita.js +2 -0
- package/dist/ishita.js.map +1 -1
- package/dist/kabir.d.ts +1 -0
- package/dist/kabir.d.ts.map +1 -1
- package/dist/kabir.js +2 -0
- package/dist/kabir.js.map +1 -1
- package/dist/meera.d.ts +1 -0
- package/dist/meera.d.ts.map +1 -1
- package/dist/meera.js +2 -0
- package/dist/meera.js.map +1 -1
- package/dist/naina.d.ts +1 -0
- package/dist/naina.d.ts.map +1 -1
- package/dist/naina.js +2 -0
- package/dist/naina.js.map +1 -1
- package/dist/playout.d.ts +42 -0
- package/dist/playout.d.ts.map +1 -0
- package/dist/playout.js +85 -0
- package/dist/playout.js.map +1 -0
- package/dist/supports.d.ts +20 -0
- package/dist/supports.d.ts.map +1 -0
- package/dist/supports.js +26 -0
- package/dist/supports.js.map +1 -0
- package/dist/three/assets.d.ts +24 -0
- package/dist/three/assets.d.ts.map +1 -0
- package/dist/three/assets.js +22 -0
- package/dist/three/assets.js.map +1 -0
- package/dist/three/budgets.d.ts +17 -0
- package/dist/three/budgets.d.ts.map +1 -0
- package/dist/three/budgets.js +21 -0
- package/dist/three/budgets.js.map +1 -0
- package/dist/three/holds.d.ts +33 -0
- package/dist/three/holds.d.ts.map +1 -0
- package/dist/three/holds.js +56 -0
- package/dist/three/holds.js.map +1 -0
- package/dist/three/internal.d.ts +52 -0
- package/dist/three/internal.d.ts.map +1 -0
- package/dist/three/internal.js +49 -0
- package/dist/three/internal.js.map +1 -0
- package/dist/three/motion-limits.json +116 -0
- package/dist/three/sequences.d.ts +71 -0
- package/dist/three/sequences.d.ts.map +1 -0
- package/dist/three/sequences.js +262 -0
- package/dist/three/sequences.js.map +1 -0
- package/dist/three/tanya.d.ts +35 -0
- package/dist/three/tanya.d.ts.map +1 -0
- package/dist/three/tanya.js +51 -0
- package/dist/three/tanya.js.map +1 -0
- package/dist/three/tara-rig.d.ts +403 -0
- package/dist/three/tara-rig.d.ts.map +1 -0
- package/dist/three/tara-rig.js +1500 -0
- package/dist/three/tara-rig.js.map +1 -0
- package/dist/three/tara.d.ts +35 -0
- package/dist/three/tara.d.ts.map +1 -0
- package/dist/three/tara.js +58 -0
- package/dist/three/tara.js.map +1 -0
- package/dist/three/tushar.d.ts +31 -0
- package/dist/three/tushar.d.ts.map +1 -0
- package/dist/three/tushar.js +47 -0
- package/dist/three/tushar.js.map +1 -0
- package/dist/types.d.ts +65 -10
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +58 -11
- package/dist/types.js.map +1 -1
- package/dist/vikram.d.ts +1 -0
- package/dist/vikram.d.ts.map +1 -1
- package/dist/vikram.js +2 -0
- package/dist/vikram.js.map +1 -1
- package/package.json +34 -6
- package/src/avatar.d.ts +59 -2
- package/src/avatar.js +743 -94
- package/src/behavior.d.ts +28 -12
- package/src/behavior.js +54 -42
- package/src/canvas/create-rig.js +1 -1
- package/src/canvas/src/live.js +2 -2
- package/src/conformance.js +1 -1
- package/src/emotions.js +4 -2
- package/src/gaze.js +334 -73
- package/src/head.js +134 -0
- package/src/idle.js +210 -19
- package/src/interjections.js +70 -26
- package/src/prosody.js +647 -0
- package/src/visemes.js +85 -15
package/src/behavior.d.ts
CHANGED
|
@@ -1,26 +1,42 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvatarStateName } from "./avatar.js";
|
|
2
2
|
|
|
3
|
-
/** The
|
|
3
|
+
/** The two renderer calls behavior resolution actually needs. */
|
|
4
|
+
export interface BehaviorAvatar {
|
|
5
|
+
setState(name: AvatarStateName): unknown;
|
|
6
|
+
/**
|
|
7
|
+
* One motion by name. The vocabulary is open — two core intents plus
|
|
8
|
+
* whatever this avatar publishes — so a name it does not have must be a
|
|
9
|
+
* no-op rather than a throw.
|
|
10
|
+
*/
|
|
11
|
+
action(id: string): unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The whole behaviour vocabulary — the nine. Not an alias list; see behavior.js.
|
|
16
|
+
*
|
|
17
|
+
* Every one of these is also an `AvatarStateName`, which is why there is no
|
|
18
|
+
* mapping table any more.
|
|
19
|
+
*/
|
|
4
20
|
export type BehaviorStateId =
|
|
5
|
-
| "IDLE" | "LISTENING" | "
|
|
21
|
+
| "IDLE" | "LISTENING" | "CANT_HEAR" | "THINKING" | "WORKING"
|
|
6
22
|
| "MUTED" | "SPEAKING" | "DEGRADED" | "OFFLINE";
|
|
7
23
|
|
|
8
|
-
export type BehaviorActionId =
|
|
9
|
-
| "ack.receive" | "ack.nod"
|
|
10
|
-
| "turn.interrupted"
|
|
11
|
-
| "gesture.greet" | "gesture.farewell" | "gesture.approve" | "gesture.wait";
|
|
24
|
+
export type BehaviorActionId = "ack" | "turn.interrupted";
|
|
12
25
|
|
|
13
|
-
export const BEHAVIOR_STATES: Readonly<Record<BehaviorStateId, { renderState: AvatarStateName }>>;
|
|
14
26
|
export const BEHAVIOR_STATE_IDS: readonly BehaviorStateId[];
|
|
15
|
-
export const BEHAVIOR_ACTIONS: Readonly<Record<BehaviorActionId, { renderAction:
|
|
27
|
+
export const BEHAVIOR_ACTIONS: Readonly<Record<BehaviorActionId, { renderAction: string }>>;
|
|
16
28
|
export const BEHAVIOR_ACTION_IDS: readonly BehaviorActionId[];
|
|
17
|
-
export const WIRE_ACTION_TO_BEHAVIOR: Readonly<Record<
|
|
29
|
+
export const WIRE_ACTION_TO_BEHAVIOR: Readonly<Record<string, BehaviorActionId>>;
|
|
18
30
|
|
|
19
31
|
export class BehaviorController {
|
|
20
|
-
constructor(avatar:
|
|
32
|
+
constructor(avatar: BehaviorAvatar);
|
|
21
33
|
state: BehaviorStateId | null;
|
|
22
34
|
setState(id: BehaviorStateId, options?: { force?: boolean }): this;
|
|
23
35
|
action(id: BehaviorActionId): this;
|
|
24
|
-
|
|
36
|
+
/**
|
|
37
|
+
* One id off the wire: a core intent through the catalog, anything else
|
|
38
|
+
* straight to the renderer. Never throws — the vocabulary is open.
|
|
39
|
+
*/
|
|
40
|
+
wireAction(id: string): this;
|
|
25
41
|
destroy(): void;
|
|
26
42
|
}
|
package/src/behavior.js
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* It is not a seam any avatar implementation sees: an implementation receives
|
|
6
6
|
* a PipecatClient and decides for itself what a state means.
|
|
7
7
|
*
|
|
8
|
-
* The vocabulary is the nine core states
|
|
9
|
-
*
|
|
8
|
+
* The vocabulary is the nine core states and two core actions. The states used
|
|
9
|
+
* to be seventeen — the core
|
|
10
|
+
* set plus every SVG render state (TYPING, SEARCHING_SCREEN, WANTS_IN, …)
|
|
10
11
|
* passed straight through, so that tooling could drive one of *those* from
|
|
11
12
|
* here. That made the mixer's private state list look like part of the
|
|
12
13
|
* behaviour vocabulary. Tooling that wants a render state calls
|
|
@@ -14,52 +15,52 @@
|
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
|
-
*
|
|
18
|
-
* asks the bundled SVG mixer for
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* The durable states this layer resolves. Each one *is* the render state it
|
|
19
|
+
* asks the bundled SVG mixer for, so there is nothing here to map.
|
|
20
|
+
*
|
|
21
|
+
* There used to be a second column — a per-state `renderState`, so a renderer
|
|
22
|
+
* could draw `WORKING` as anything it liked and the divergence would land here
|
|
23
|
+
* rather than leak. In nine rows it had exactly one non-identity entry,
|
|
24
|
+
* `STRAINING -> CANT_HEAR`, and the wire redesign deleted that entry by
|
|
25
|
+
* renaming the state: a state names the bot's *situation*, never how it looks,
|
|
26
|
+
* and "straining" was a situation named after one drawing of it. A table whose
|
|
27
|
+
* every row reads `X: X` documents nothing and invites a renderer to plug into
|
|
28
|
+
* it, so it is a list now. A renderer that genuinely wants to draw one of these
|
|
29
|
+
* as something else calls `avatar.setState` with the render state it wants,
|
|
30
|
+
* which is whose state that is.
|
|
22
31
|
*/
|
|
23
|
-
export const
|
|
24
|
-
IDLE
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// behavior name claims only that the avatar is trying harder to hear, while
|
|
28
|
-
// the pose is one particular drawing of that. A renderer with no such pose
|
|
29
|
-
// may legitimately point this at LISTENING.
|
|
30
|
-
STRAINING: { renderState: 'CANT_HEAR' },
|
|
31
|
-
THINKING: { renderState: 'THINKING' },
|
|
32
|
-
WORKING: { renderState: 'WORKING' },
|
|
33
|
-
MUTED: { renderState: 'MUTED' },
|
|
34
|
-
SPEAKING: { renderState: 'SPEAKING' },
|
|
35
|
-
DEGRADED: { renderState: 'DEGRADED' },
|
|
36
|
-
OFFLINE: { renderState: 'OFFLINE' },
|
|
37
|
-
});
|
|
32
|
+
export const BEHAVIOR_STATE_IDS = Object.freeze([
|
|
33
|
+
'IDLE', 'LISTENING', 'CANT_HEAR', 'THINKING', 'WORKING',
|
|
34
|
+
'MUTED', 'SPEAKING', 'DEGRADED', 'OFFLINE',
|
|
35
|
+
]);
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
const STATE_IDS = new Set(BEHAVIOR_STATE_IDS);
|
|
40
38
|
|
|
41
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* Library action IDs: the two intents every renderer owes a server, in this
|
|
41
|
+
* layer's readable spelling.
|
|
42
|
+
*
|
|
43
|
+
* It used to be seven, with a `sequence` command alongside for a renderer's own
|
|
44
|
+
* motions. The seven were not the wrong *names* so much as the wrong idea: four
|
|
45
|
+
* of them were `GESTURE_*` — a greet, a goodbye, a wave — which are things a
|
|
46
|
+
* particular body does and not intents a server can hold every face to, and two
|
|
47
|
+
* were the receipt and the nod, which are two *shapes* of acknowledging. So the
|
|
48
|
+
* server now says only that an acknowledgement is due, the avatar picks which
|
|
49
|
+
* one it makes, and the gestures live in whichever catalogue can draw them,
|
|
50
|
+
* reachable by the same open `action` id a sequence used to need its own
|
|
51
|
+
* command for.
|
|
52
|
+
*/
|
|
42
53
|
export const BEHAVIOR_ACTIONS = Object.freeze({
|
|
43
|
-
|
|
44
|
-
'ack.nod': { renderAction: 'ACK_NOD' },
|
|
54
|
+
ack: { renderAction: 'ACKNOWLEDGE' },
|
|
45
55
|
'turn.interrupted': { renderAction: 'RESPONSE_INTERRUPTED' },
|
|
46
|
-
'gesture.greet': { renderAction: 'GESTURE_GREET' },
|
|
47
|
-
'gesture.farewell': { renderAction: 'GESTURE_GOODBYE' },
|
|
48
|
-
'gesture.approve': { renderAction: 'GESTURE_APPROVE' },
|
|
49
|
-
'gesture.wait': { renderAction: 'GESTURE_WAIT' },
|
|
50
56
|
});
|
|
51
57
|
|
|
52
58
|
export const BEHAVIOR_ACTION_IDS = Object.freeze(Object.keys(BEHAVIOR_ACTIONS));
|
|
53
59
|
|
|
54
|
-
/**
|
|
60
|
+
/** The two core wire names map into the behavior catalog here and nowhere else. */
|
|
55
61
|
export const WIRE_ACTION_TO_BEHAVIOR = Object.freeze({
|
|
56
|
-
|
|
57
|
-
ACK_NOD: 'ack.nod',
|
|
62
|
+
ACKNOWLEDGE: 'ack',
|
|
58
63
|
RESPONSE_INTERRUPTED: 'turn.interrupted',
|
|
59
|
-
GESTURE_GREET: 'gesture.greet',
|
|
60
|
-
GESTURE_GOODBYE: 'gesture.farewell',
|
|
61
|
-
GESTURE_APPROVE: 'gesture.approve',
|
|
62
|
-
GESTURE_WAIT: 'gesture.wait',
|
|
63
64
|
});
|
|
64
65
|
|
|
65
66
|
/**
|
|
@@ -77,11 +78,10 @@ export class BehaviorController {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
setState(id, { force = false } = {}) {
|
|
80
|
-
|
|
81
|
-
if (!def) throw new Error(`unknown behavior state: ${id}`);
|
|
81
|
+
if (!STATE_IDS.has(id)) throw new Error(`unknown behavior state: ${id}`);
|
|
82
82
|
if (!force && this.state === id) return this;
|
|
83
83
|
this.state = id;
|
|
84
|
-
this.avatar.setState(
|
|
84
|
+
this.avatar.setState(id);
|
|
85
85
|
return this;
|
|
86
86
|
}
|
|
87
87
|
|
|
@@ -94,9 +94,21 @@ export class BehaviorController {
|
|
|
94
94
|
return this;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
/**
|
|
98
|
+
* One id off the wire. A core one goes through the behavior catalog; any
|
|
99
|
+
* other goes straight to the renderer, and that asymmetry is the point.
|
|
100
|
+
* `BEHAVIOR_ACTIONS` exists so a portable intent has a readable name
|
|
101
|
+
* independent of how any one face renders it. An avatar's own id has no such
|
|
102
|
+
* independence — it *is* the rendering, named by the renderer that owns it —
|
|
103
|
+
* so giving it a behavior alias would claim a portability it does not have.
|
|
104
|
+
*
|
|
105
|
+
* Neither path throws. The wire's action vocabulary is open and an id this
|
|
106
|
+
* face cannot draw is the expected case, not somebody's broken build.
|
|
107
|
+
*/
|
|
97
108
|
wireAction(id) {
|
|
98
109
|
const behaviorId = WIRE_ACTION_TO_BEHAVIOR[id];
|
|
99
|
-
if (
|
|
100
|
-
|
|
110
|
+
if (behaviorId) return this.action(behaviorId);
|
|
111
|
+
this.avatar.action(id);
|
|
112
|
+
return this;
|
|
101
113
|
}
|
|
102
114
|
}
|
package/src/canvas/create-rig.js
CHANGED
|
@@ -73,7 +73,7 @@ export function createCanvasRig({ url, images, face, label }) {
|
|
|
73
73
|
renderer.resize(dpr);
|
|
74
74
|
live = createLive(rig, faceModule);
|
|
75
75
|
}).catch((error) => {
|
|
76
|
-
if (!destroyed) console.error('[
|
|
76
|
+
if (!destroyed) console.error('[avatar] canvas rig failed to load', error);
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
return {
|
package/src/canvas/src/live.js
CHANGED
|
@@ -120,8 +120,8 @@ const num = (v, d) => (typeof v === 'number' && v === v ? v : d);
|
|
|
120
120
|
// degree and a half about the sternum, which is exactly what you get when the
|
|
121
121
|
// torso is one filled path. So the four are spent the way `poseTransforms`
|
|
122
122
|
// spends them, with each avatar's own travels in `face.mjs BODY_LIVE`, and the
|
|
123
|
-
// state that made this visible —
|
|
124
|
-
//
|
|
123
|
+
// state that made this visible — `CANT_HEAR`, which holds `torsoLean` at
|
|
124
|
+
// 0.70 — leans in instead of standing straight.
|
|
125
125
|
//
|
|
126
126
|
// AND THE TRUNK'S SHARE OF THE HEAD'S OWN CHANNELS, which is the rest of what
|
|
127
127
|
// `poseTransforms` does and was left out the first time round. Three things,
|
package/src/conformance.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* drawing before anyone is asked to look at it.
|
|
14
14
|
*
|
|
15
15
|
* `advance` is the seam that lets one copy of the sweep serve both callers:
|
|
16
|
-
*
|
|
16
|
+
* a rig page in the working tree lets real time pass and watches it happen, while
|
|
17
17
|
* `packages/avatar/test/conformance.test.ts` steps `{manual: true}` avatars by a fixed
|
|
18
18
|
* dt and finishes in milliseconds. The sweep's own clock is handed to `speak`
|
|
19
19
|
* so the mouth articulates under both — a stepped avatar reading
|
package/src/emotions.js
CHANGED
|
@@ -14,8 +14,10 @@ export const EMOTIONS = {
|
|
|
14
14
|
neutral: {},
|
|
15
15
|
warm: {
|
|
16
16
|
mouthCornerL: 0.48, mouthCornerR: 0.48,
|
|
17
|
-
// A real smile squints. Without this it reads as a mask.
|
|
18
|
-
|
|
17
|
+
// A real smile squints. Without this it reads as a mask. From below —
|
|
18
|
+
// the cheek pushes the lower lid up (AU6) — with no upper lid dropped on
|
|
19
|
+
// top, which on a photographic eye read as sleepy rather than warm.
|
|
20
|
+
squintL: 0.30, squintR: 0.30,
|
|
19
21
|
browRaiseL: 0.10, browRaiseR: 0.10,
|
|
20
22
|
},
|
|
21
23
|
curious: {
|