@voqalize/avatar 0.2.2 → 0.3.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.
Files changed (201) hide show
  1. package/LICENSE +21 -661
  2. package/README.md +144 -663
  3. package/client/Avatar.tsx +57 -0
  4. package/client/AvatarClient.ts +523 -0
  5. package/client/arjun.ts +24 -0
  6. package/client/createAvatar.ts +139 -0
  7. package/client/createCanvasAvatar.ts +72 -0
  8. package/client/index.ts +39 -0
  9. package/client/internal.ts +80 -0
  10. package/client/interviewer-female.ts +4 -0
  11. package/client/interviewer-male.ts +4 -0
  12. package/client/ishita.ts +24 -0
  13. package/client/kabir.ts +24 -0
  14. package/client/meera.ts +24 -0
  15. package/client/naina.ts +24 -0
  16. package/client/professional-female-a.ts +4 -0
  17. package/client/professional-female-b.ts +4 -0
  18. package/client/professional-male-a.ts +4 -0
  19. package/client/professional-male-b.ts +4 -0
  20. package/client/react.ts +13 -0
  21. package/client/types.ts +146 -0
  22. package/client/useAvatar.ts +74 -0
  23. package/client/vikram.ts +24 -0
  24. package/dist/Avatar.d.ts +44 -0
  25. package/dist/Avatar.d.ts.map +1 -0
  26. package/{client/dist → dist}/Avatar.js +2 -2
  27. package/dist/Avatar.js.map +1 -0
  28. package/dist/AvatarClient.d.ts +196 -0
  29. package/dist/AvatarClient.d.ts.map +1 -0
  30. package/dist/AvatarClient.js +450 -0
  31. package/dist/AvatarClient.js.map +1 -0
  32. package/dist/arjun.d.ts +6 -0
  33. package/dist/arjun.d.ts.map +1 -0
  34. package/dist/arjun.js +18 -0
  35. package/dist/arjun.js.map +1 -0
  36. package/dist/createAvatar.d.ts +99 -0
  37. package/dist/createAvatar.d.ts.map +1 -0
  38. package/dist/createAvatar.js +75 -0
  39. package/dist/createAvatar.js.map +1 -0
  40. package/dist/createCanvasAvatar.d.ts +22 -0
  41. package/dist/createCanvasAvatar.d.ts.map +1 -0
  42. package/dist/createCanvasAvatar.js +47 -0
  43. package/dist/createCanvasAvatar.js.map +1 -0
  44. package/dist/index.d.ts +30 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +29 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/internal.d.ts +30 -0
  49. package/dist/internal.d.ts.map +1 -0
  50. package/dist/internal.js +33 -0
  51. package/dist/internal.js.map +1 -0
  52. package/dist/interviewer-female.d.ts +4 -0
  53. package/dist/interviewer-female.d.ts.map +1 -0
  54. package/dist/interviewer-female.js +3 -0
  55. package/dist/interviewer-female.js.map +1 -0
  56. package/dist/interviewer-male.d.ts +4 -0
  57. package/dist/interviewer-male.d.ts.map +1 -0
  58. package/dist/interviewer-male.js +3 -0
  59. package/dist/interviewer-male.js.map +1 -0
  60. package/dist/ishita.d.ts +6 -0
  61. package/dist/ishita.d.ts.map +1 -0
  62. package/dist/ishita.js +18 -0
  63. package/dist/ishita.js.map +1 -0
  64. package/dist/kabir.d.ts +6 -0
  65. package/dist/kabir.d.ts.map +1 -0
  66. package/dist/kabir.js +18 -0
  67. package/dist/kabir.js.map +1 -0
  68. package/dist/meera.d.ts +6 -0
  69. package/dist/meera.d.ts.map +1 -0
  70. package/dist/meera.js +18 -0
  71. package/dist/meera.js.map +1 -0
  72. package/dist/naina.d.ts +6 -0
  73. package/dist/naina.d.ts.map +1 -0
  74. package/dist/naina.js +18 -0
  75. package/dist/naina.js.map +1 -0
  76. package/dist/professional-female-a.d.ts +4 -0
  77. package/dist/professional-female-a.d.ts.map +1 -0
  78. package/dist/professional-female-a.js +3 -0
  79. package/dist/professional-female-a.js.map +1 -0
  80. package/dist/professional-female-b.d.ts +4 -0
  81. package/dist/professional-female-b.d.ts.map +1 -0
  82. package/dist/professional-female-b.js +3 -0
  83. package/dist/professional-female-b.js.map +1 -0
  84. package/dist/professional-male-a.d.ts +4 -0
  85. package/dist/professional-male-a.d.ts.map +1 -0
  86. package/dist/professional-male-a.js +3 -0
  87. package/dist/professional-male-a.js.map +1 -0
  88. package/dist/professional-male-b.d.ts +4 -0
  89. package/dist/professional-male-b.d.ts.map +1 -0
  90. package/dist/professional-male-b.js +3 -0
  91. package/dist/professional-male-b.js.map +1 -0
  92. package/dist/react.d.ts +13 -0
  93. package/dist/react.d.ts.map +1 -0
  94. package/dist/react.js +13 -0
  95. package/dist/react.js.map +1 -0
  96. package/dist/types.d.ts +94 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +93 -0
  99. package/dist/types.js.map +1 -0
  100. package/dist/useAvatar.d.ts +39 -0
  101. package/dist/useAvatar.d.ts.map +1 -0
  102. package/dist/useAvatar.js +35 -0
  103. package/dist/useAvatar.js.map +1 -0
  104. package/dist/vikram.d.ts +6 -0
  105. package/dist/vikram.d.ts.map +1 -0
  106. package/dist/vikram.js +18 -0
  107. package/dist/vikram.js.map +1 -0
  108. package/package.json +81 -24
  109. package/src/avatar.d.ts +161 -120
  110. package/src/avatar.js +234 -146
  111. package/src/behavior.d.ts +26 -0
  112. package/src/behavior.js +102 -0
  113. package/src/camera.js +29 -0
  114. package/src/canvas/author/parts/eye.mjs +722 -0
  115. package/src/canvas/author/parts/hand.mjs +1156 -0
  116. package/src/canvas/author/parts/mouth.mjs +741 -0
  117. package/src/canvas/author/parts/nose.mjs +100 -0
  118. package/src/canvas/author/parts/skin-detail.mjs +67 -0
  119. package/src/canvas/author/path.mjs +283 -0
  120. package/src/canvas/author/rig.mjs +405 -0
  121. package/src/canvas/avatars/round/face.d.mts +3 -0
  122. package/src/canvas/avatars/round/face.mjs +1307 -0
  123. package/src/canvas/create-rig.d.ts +15 -0
  124. package/src/canvas/create-rig.js +100 -0
  125. package/src/canvas/data/img/professional-female-a-hair-back.webp +0 -0
  126. package/src/canvas/data/img/professional-female-a-hair-front.webp +0 -0
  127. package/src/canvas/data/img/professional-female-a-top-body.webp +0 -0
  128. package/src/canvas/data/img/professional-female-b-hair-back.webp +0 -0
  129. package/src/canvas/data/img/professional-female-b-hair-front.webp +0 -0
  130. package/src/canvas/data/img/professional-female-b-top-body.webp +0 -0
  131. package/src/canvas/data/img/professional-male-a-hair-back.webp +0 -0
  132. package/src/canvas/data/img/professional-male-a-hair-front.webp +0 -0
  133. package/src/canvas/data/img/professional-male-a-top-body.webp +0 -0
  134. package/src/canvas/data/img/professional-male-b-hair-back.webp +0 -0
  135. package/src/canvas/data/img/professional-male-b-hair-front.webp +0 -0
  136. package/src/canvas/data/img/professional-male-b-top-body.webp +0 -0
  137. package/src/canvas/data/img/round-m3-hair-back.webp +0 -0
  138. package/src/canvas/data/img/round-m3-hair-front.webp +0 -0
  139. package/src/canvas/data/img/round-m3-top-body.webp +0 -0
  140. package/src/canvas/data/img/round-w1-hair-back.webp +0 -0
  141. package/src/canvas/data/img/round-w1-hair-front.webp +0 -0
  142. package/src/canvas/data/img/round-w1-top-body.webp +0 -0
  143. package/src/canvas/data/interviewer-female.rig.json +1 -0
  144. package/src/canvas/data/interviewer-male.rig.json +1 -0
  145. package/src/canvas/data/professional-female-a.rig.json +1 -0
  146. package/src/canvas/data/professional-female-b.rig.json +1 -0
  147. package/src/canvas/data/professional-male-a.rig.json +1 -0
  148. package/src/canvas/data/professional-male-b.rig.json +1 -0
  149. package/src/canvas/src/live.js +508 -0
  150. package/src/canvas/src/render2d.js +218 -0
  151. package/src/canvas/src/rig.js +297 -0
  152. package/src/canvas/src/vocab.js +96 -0
  153. package/src/clips.js +18 -7
  154. package/src/conformance.js +119 -0
  155. package/src/emotions.js +4 -3
  156. package/src/face-core.js +27 -1
  157. package/src/face-myna.d.ts +7 -0
  158. package/src/face-myna.js +192 -131
  159. package/src/face-peep-control-plane.js +167 -0
  160. package/src/face-peep.d.ts +7 -0
  161. package/src/face-peep.js +274 -116
  162. package/src/face-wren.d.ts +7 -0
  163. package/src/face-wren.js +17 -19
  164. package/src/faces.d.ts +12 -0
  165. package/src/faces.js +53 -0
  166. package/src/gaze.js +119 -4
  167. package/src/hand.js +120 -91
  168. package/src/idle.js +18 -101
  169. package/src/interjections.js +156 -31
  170. package/src/params.js +8 -4
  171. package/src/perform.js +7 -9
  172. package/src/rig.d.ts +24 -0
  173. package/src/rig.js +32 -0
  174. package/src/speech-timing.js +23 -0
  175. package/src/visemes.js +28 -12
  176. package/client/dist/Avatar.d.ts +0 -27
  177. package/client/dist/Avatar.d.ts.map +0 -1
  178. package/client/dist/Avatar.js.map +0 -1
  179. package/client/dist/AvatarClient.d.ts +0 -139
  180. package/client/dist/AvatarClient.d.ts.map +0 -1
  181. package/client/dist/AvatarClient.js +0 -223
  182. package/client/dist/AvatarClient.js.map +0 -1
  183. package/client/dist/index.d.ts +0 -22
  184. package/client/dist/index.d.ts.map +0 -1
  185. package/client/dist/index.js +0 -22
  186. package/client/dist/index.js.map +0 -1
  187. package/client/dist/types.d.ts +0 -86
  188. package/client/dist/types.d.ts.map +0 -1
  189. package/client/dist/types.js +0 -31
  190. package/client/dist/types.js.map +0 -1
  191. package/client/dist/useAvatar.d.ts +0 -42
  192. package/client/dist/useAvatar.d.ts.map +0 -1
  193. package/client/dist/useAvatar.js +0 -51
  194. package/client/dist/useAvatar.js.map +0 -1
  195. package/client/src/Avatar.tsx +0 -33
  196. package/client/src/AvatarClient.ts +0 -270
  197. package/client/src/index.ts +0 -22
  198. package/client/src/types.ts +0 -104
  199. package/client/src/useAvatar.ts +0 -85
  200. package/docs/contract-avatar.md +0 -371
  201. package/docs/contract-protocol.md +0 -461
@@ -0,0 +1,26 @@
1
+ import type { AvatarApi, AvatarActionId, AvatarStateName } from "./avatar.js";
2
+
3
+ /** The whole behaviour vocabulary — the nine. Not an alias list; see behavior.js. */
4
+ export type BehaviorStateId =
5
+ | "IDLE" | "LISTENING" | "STRAINING" | "THINKING" | "WORKING"
6
+ | "MUTED" | "SPEAKING" | "DEGRADED" | "OFFLINE";
7
+
8
+ export type BehaviorActionId =
9
+ | "ack.receive" | "ack.nod"
10
+ | "turn.interrupted"
11
+ | "gesture.greet" | "gesture.farewell" | "gesture.approve" | "gesture.wait";
12
+
13
+ export const BEHAVIOR_STATES: Readonly<Record<BehaviorStateId, { renderState: AvatarStateName }>>;
14
+ export const BEHAVIOR_STATE_IDS: readonly BehaviorStateId[];
15
+ export const BEHAVIOR_ACTIONS: Readonly<Record<BehaviorActionId, { renderAction: AvatarActionId }>>;
16
+ export const BEHAVIOR_ACTION_IDS: readonly BehaviorActionId[];
17
+ export const WIRE_ACTION_TO_BEHAVIOR: Readonly<Record<AvatarActionId, BehaviorActionId>>;
18
+
19
+ export class BehaviorController {
20
+ constructor(avatar: AvatarApi);
21
+ state: BehaviorStateId | null;
22
+ setState(id: BehaviorStateId, options?: { force?: boolean }): this;
23
+ action(id: BehaviorActionId): this;
24
+ wireAction(id: AvatarActionId): this;
25
+ destroy(): void;
26
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Behavior catalog and controller.
3
+ *
4
+ * This is the layer between factual/wire inputs and the bundled SVG renderer.
5
+ * It is not a seam any avatar implementation sees: an implementation receives
6
+ * a PipecatClient and decides for itself what a state means.
7
+ *
8
+ * The vocabulary is the nine core states. It used to be seventeen — the core
9
+ * set plus every SVG render state (TYPING, CANT_HEAR, SEARCHING_SCREEN, …)
10
+ * passed straight through, so that tooling could drive one of *those* from
11
+ * here. That made the mixer's private state list look like part of the
12
+ * behaviour vocabulary. Tooling that wants a render state calls
13
+ * `avatar.setState` on the mixer, which is whose state it is.
14
+ */
15
+
16
+ /**
17
+ * Durable states the behavior layer resolves, and the render state each one
18
+ * asks the bundled SVG mixer for. The right-hand column belongs to a renderer:
19
+ * `WORKING` may draw as anything, and this is where a divergence lands rather
20
+ * than leaks. `STRAINING` is the first row to actually use that — the two
21
+ * columns were written out for years while the mapping stayed 1:1.
22
+ */
23
+ export const BEHAVIOR_STATES = Object.freeze({
24
+ IDLE: { renderState: 'IDLE' },
25
+ LISTENING: { renderState: 'LISTENING' },
26
+ // What the server calls straining, this renderer draws as CANT_HEAR: the
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
+ });
38
+
39
+ export const BEHAVIOR_STATE_IDS = Object.freeze(Object.keys(BEHAVIOR_STATES));
40
+
41
+ /** Library action IDs. These are broader and more readable than the wire. */
42
+ export const BEHAVIOR_ACTIONS = Object.freeze({
43
+ 'ack.receive': { renderAction: 'ACK_RECEIVE' },
44
+ 'ack.nod': { renderAction: 'ACK_NOD' },
45
+ '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
+ });
51
+
52
+ export const BEHAVIOR_ACTION_IDS = Object.freeze(Object.keys(BEHAVIOR_ACTIONS));
53
+
54
+ /** Stable, promoted wire names map into the behavior catalog here and nowhere else. */
55
+ export const WIRE_ACTION_TO_BEHAVIOR = Object.freeze({
56
+ ACK_RECEIVE: 'ack.receive',
57
+ ACK_NOD: 'ack.nod',
58
+ 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
+
65
+ /**
66
+ * Turns behavior intent into the legacy SVG mixer's calls. A future renderer
67
+ * receives the same intent through its own adapter; it does not need to know
68
+ * the legacy state/action names used below.
69
+ */
70
+ export class BehaviorController {
71
+ constructor(avatar) {
72
+ if (!avatar || typeof avatar.setState !== 'function' || typeof avatar.action !== 'function') {
73
+ throw new TypeError('BehaviorController requires an avatar implementation');
74
+ }
75
+ this.avatar = avatar;
76
+ this.state = null;
77
+ }
78
+
79
+ setState(id, { force = false } = {}) {
80
+ const def = BEHAVIOR_STATES[id];
81
+ if (!def) throw new Error(`unknown behavior state: ${id}`);
82
+ if (!force && this.state === id) return this;
83
+ this.state = id;
84
+ this.avatar.setState(def.renderState);
85
+ return this;
86
+ }
87
+
88
+ destroy() {}
89
+
90
+ action(id) {
91
+ const def = BEHAVIOR_ACTIONS[id];
92
+ if (!def) throw new Error(`unknown behavior action: ${id}`);
93
+ this.avatar.action(def.renderAction);
94
+ return this;
95
+ }
96
+
97
+ wireAction(id) {
98
+ const behaviorId = WIRE_ACTION_TO_BEHAVIOR[id];
99
+ if (!behaviorId) throw new Error(`unknown wire action: ${id}`);
100
+ return this.action(behaviorId);
101
+ }
102
+ }
package/src/camera.js ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The close webcam crop shared by every avatar renderer.
3
+ *
4
+ * The head gets most of the tile because lip sync and listening expressions
5
+ * are the information the avatar is there to carry. The remaining lower band
6
+ * leaves enough neck and shoulder for posture to read without turning the
7
+ * call tile into a half-body portrait.
8
+ */
9
+ export const CALL_CAMERA = Object.freeze({
10
+ aspect: 4 / 3,
11
+ headroom: 0.06,
12
+ head: 0.70,
13
+ body: 0.24,
14
+ });
15
+
16
+ /** Derive a camera rectangle from three landmarks in the drawing's own units. */
17
+ export function viewBoxForHead({ centerX, crownY, chinY }) {
18
+ if (![centerX, crownY, chinY].every(Number.isFinite) || chinY <= crownY) {
19
+ throw new TypeError('viewBoxForHead: expected a finite centre and crown above chin');
20
+ }
21
+ const h = (chinY - crownY) / CALL_CAMERA.head;
22
+ const w = h * CALL_CAMERA.aspect;
23
+ return {
24
+ x: centerX - w / 2,
25
+ y: crownY - h * CALL_CAMERA.headroom,
26
+ w,
27
+ h,
28
+ };
29
+ }