@voqalize/avatar 0.4.2 → 0.4.4
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-APACHE-2.0 +201 -0
- package/README.md +11 -98
- package/assets/README.md +28 -17
- package/assets/tanvi.glb +0 -0
- package/assets/tanya.glb +0 -0
- package/assets/tara.glb +0 -0
- package/assets/tess.glb +0 -0
- package/assets/tushar.glb +0 -0
- package/client/internal.ts +4 -0
- package/client/supports.ts +6 -7
- package/client/three/assets.ts +2 -0
- package/client/three/budgets.ts +1 -1
- package/client/three/{tara-rig.ts → character-rig.ts} +708 -547
- package/client/three/createCharacter.ts +109 -0
- package/client/three/holds.ts +1 -1
- package/client/three/internal.ts +4 -3
- package/client/three/motion-limits.json +5 -1
- package/client/three/tanvi-asset.ts +6 -0
- package/client/three/tanvi.ts +30 -0
- package/client/three/tanya.ts +18 -58
- package/client/three/tara.ts +19 -60
- package/client/three/tess.ts +17 -59
- package/client/three/tushar.ts +18 -55
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +5 -1
- package/dist/internal.js.map +1 -1
- package/dist/supports.d.ts +6 -7
- package/dist/supports.d.ts.map +1 -1
- package/dist/supports.js +6 -7
- package/dist/supports.js.map +1 -1
- package/dist/three/assets.d.ts +1 -0
- package/dist/three/assets.d.ts.map +1 -1
- package/dist/three/assets.js +2 -0
- package/dist/three/assets.js.map +1 -1
- package/dist/three/budgets.d.ts +1 -1
- package/dist/three/budgets.js +1 -1
- package/dist/three/character-rig.d.ts +366 -0
- package/dist/three/character-rig.d.ts.map +1 -0
- package/dist/three/{tara-rig.js → character-rig.js} +664 -525
- package/dist/three/character-rig.js.map +1 -0
- package/dist/three/createCharacter.d.ts +60 -0
- package/dist/three/createCharacter.d.ts.map +1 -0
- package/dist/three/createCharacter.js +84 -0
- package/dist/three/createCharacter.js.map +1 -0
- package/dist/three/holds.js +1 -1
- package/dist/three/holds.js.map +1 -1
- package/dist/three/internal.d.ts +3 -3
- package/dist/three/internal.d.ts.map +1 -1
- package/dist/three/internal.js +2 -2
- package/dist/three/internal.js.map +1 -1
- package/dist/three/motion-limits.json +5 -1
- package/dist/three/tanvi-asset.d.ts +7 -0
- package/dist/three/tanvi-asset.d.ts.map +1 -0
- package/dist/three/tanvi-asset.js +7 -0
- package/dist/three/tanvi-asset.js.map +1 -0
- package/dist/three/tanvi.d.ts +24 -0
- package/dist/three/tanvi.d.ts.map +1 -0
- package/dist/three/tanvi.js +24 -0
- package/dist/three/tanvi.js.map +1 -0
- package/dist/three/tanya.d.ts +16 -28
- package/dist/three/tanya.d.ts.map +1 -1
- package/dist/three/tanya.js +15 -43
- package/dist/three/tanya.js.map +1 -1
- package/dist/three/tara.d.ts +17 -22
- package/dist/three/tara.d.ts.map +1 -1
- package/dist/three/tara.js +16 -45
- package/dist/three/tara.js.map +1 -1
- package/dist/three/tess.d.ts +15 -29
- package/dist/three/tess.d.ts.map +1 -1
- package/dist/three/tess.js +14 -44
- package/dist/three/tess.js.map +1 -1
- package/dist/three/tushar.d.ts +16 -25
- package/dist/three/tushar.d.ts.map +1 -1
- package/dist/three/tushar.js +15 -40
- package/dist/three/tushar.js.map +1 -1
- package/package.json +7 -50
- package/src/avatar.d.ts +2 -0
- package/src/avatar.js +99 -220
- package/src/gaze.js +1 -3
- package/src/idle.js +10 -2
- package/src/prosody.js +7 -3
- package/src/speech-timing.js +28 -0
- package/src/visemes.js +107 -13
- package/client/arjun.ts +0 -26
- package/client/createCanvasAvatar.ts +0 -72
- package/client/interviewer-female.ts +0 -4
- package/client/interviewer-male.ts +0 -4
- package/client/ishita.ts +0 -26
- package/client/kabir.ts +0 -26
- package/client/meera.ts +0 -26
- package/client/naina.ts +0 -26
- package/client/professional-female-a.ts +0 -4
- package/client/professional-female-b.ts +0 -4
- package/client/professional-male-a.ts +0 -4
- package/client/professional-male-b.ts +0 -4
- package/client/vikram.ts +0 -26
- package/dist/arjun.d.ts +0 -7
- package/dist/arjun.d.ts.map +0 -1
- package/dist/arjun.js +0 -20
- package/dist/arjun.js.map +0 -1
- package/dist/createCanvasAvatar.d.ts +0 -22
- package/dist/createCanvasAvatar.d.ts.map +0 -1
- package/dist/createCanvasAvatar.js +0 -47
- package/dist/createCanvasAvatar.js.map +0 -1
- package/dist/interviewer-female.d.ts +0 -4
- package/dist/interviewer-female.d.ts.map +0 -1
- package/dist/interviewer-female.js +0 -3
- package/dist/interviewer-female.js.map +0 -1
- package/dist/interviewer-male.d.ts +0 -4
- package/dist/interviewer-male.d.ts.map +0 -1
- package/dist/interviewer-male.js +0 -3
- package/dist/interviewer-male.js.map +0 -1
- package/dist/ishita.d.ts +0 -7
- package/dist/ishita.d.ts.map +0 -1
- package/dist/ishita.js +0 -20
- package/dist/ishita.js.map +0 -1
- package/dist/kabir.d.ts +0 -7
- package/dist/kabir.d.ts.map +0 -1
- package/dist/kabir.js +0 -20
- package/dist/kabir.js.map +0 -1
- package/dist/meera.d.ts +0 -7
- package/dist/meera.d.ts.map +0 -1
- package/dist/meera.js +0 -20
- package/dist/meera.js.map +0 -1
- package/dist/naina.d.ts +0 -7
- package/dist/naina.d.ts.map +0 -1
- package/dist/naina.js +0 -20
- package/dist/naina.js.map +0 -1
- package/dist/professional-female-a.d.ts +0 -4
- package/dist/professional-female-a.d.ts.map +0 -1
- package/dist/professional-female-a.js +0 -3
- package/dist/professional-female-a.js.map +0 -1
- package/dist/professional-female-b.d.ts +0 -4
- package/dist/professional-female-b.d.ts.map +0 -1
- package/dist/professional-female-b.js +0 -3
- package/dist/professional-female-b.js.map +0 -1
- package/dist/professional-male-a.d.ts +0 -4
- package/dist/professional-male-a.d.ts.map +0 -1
- package/dist/professional-male-a.js +0 -3
- package/dist/professional-male-a.js.map +0 -1
- package/dist/professional-male-b.d.ts +0 -4
- package/dist/professional-male-b.d.ts.map +0 -1
- package/dist/professional-male-b.js +0 -3
- package/dist/professional-male-b.js.map +0 -1
- package/dist/three/tara-rig.d.ts +0 -423
- package/dist/three/tara-rig.d.ts.map +0 -1
- package/dist/three/tara-rig.js.map +0 -1
- package/dist/vikram.d.ts +0 -7
- package/dist/vikram.d.ts.map +0 -1
- package/dist/vikram.js +0 -20
- package/dist/vikram.js.map +0 -1
- package/src/canvas/author/parts/eye.mjs +0 -722
- package/src/canvas/author/parts/hand.mjs +0 -1156
- package/src/canvas/author/parts/mouth.mjs +0 -741
- package/src/canvas/author/parts/nose.mjs +0 -100
- package/src/canvas/author/parts/skin-detail.mjs +0 -67
- package/src/canvas/author/path.mjs +0 -283
- package/src/canvas/author/rig.mjs +0 -405
- package/src/canvas/avatars/round/face.d.mts +0 -3
- package/src/canvas/avatars/round/face.mjs +0 -1307
- package/src/canvas/create-rig.d.ts +0 -15
- package/src/canvas/create-rig.js +0 -100
- package/src/canvas/data/img/professional-female-a-hair-back.webp +0 -0
- package/src/canvas/data/img/professional-female-a-hair-front.webp +0 -0
- package/src/canvas/data/img/professional-female-a-top-body.webp +0 -0
- package/src/canvas/data/img/professional-female-b-hair-back.webp +0 -0
- package/src/canvas/data/img/professional-female-b-hair-front.webp +0 -0
- package/src/canvas/data/img/professional-female-b-top-body.webp +0 -0
- package/src/canvas/data/img/professional-male-a-hair-back.webp +0 -0
- package/src/canvas/data/img/professional-male-a-hair-front.webp +0 -0
- package/src/canvas/data/img/professional-male-a-top-body.webp +0 -0
- package/src/canvas/data/img/professional-male-b-hair-back.webp +0 -0
- package/src/canvas/data/img/professional-male-b-hair-front.webp +0 -0
- package/src/canvas/data/img/professional-male-b-top-body.webp +0 -0
- package/src/canvas/data/img/round-m3-hair-back.webp +0 -0
- package/src/canvas/data/img/round-m3-hair-front.webp +0 -0
- package/src/canvas/data/img/round-m3-top-body.webp +0 -0
- package/src/canvas/data/img/round-w1-hair-back.webp +0 -0
- package/src/canvas/data/img/round-w1-hair-front.webp +0 -0
- package/src/canvas/data/img/round-w1-top-body.webp +0 -0
- package/src/canvas/data/interviewer-female.rig.json +0 -1
- package/src/canvas/data/interviewer-male.rig.json +0 -1
- package/src/canvas/data/professional-female-a.rig.json +0 -1
- package/src/canvas/data/professional-female-b.rig.json +0 -1
- package/src/canvas/data/professional-male-a.rig.json +0 -1
- package/src/canvas/data/professional-male-b.rig.json +0 -1
- package/src/canvas/src/live.js +0 -508
- package/src/canvas/src/render2d.js +0 -218
- package/src/canvas/src/rig.js +0 -297
- package/src/canvas/src/vocab.js +0 -96
package/src/idle.js
CHANGED
|
@@ -77,6 +77,14 @@ const rand = ([a, b]) => a + Math.random() * (b - a);
|
|
|
77
77
|
// is two large gaze shifts a second apart and carries one blink, not two; and
|
|
78
78
|
// a state change that lands on a gaze shift is one event to the eyelids.
|
|
79
79
|
const BLINK_REFRACTORY = 1.5;
|
|
80
|
+
// How long an ordinary blink takes, open to open, in seconds. Centred on the
|
|
81
|
+
// 208 ms "short" blink Hömke et al. gave their avatar listener
|
|
82
|
+
// (research-biomechanics.md §3.6) — the one their listeners read as nothing
|
|
83
|
+
// but a blink. It was 0.11–0.15 s, and in a recorded call on 2026-09-24 a
|
|
84
|
+
// photographic lid covered that in two or three frames: open, shut, open, a
|
|
85
|
+
// pop rather than a lid travelling, which is the flicker a reviewer calls
|
|
86
|
+
// ghosting. A line face swallowed it; a photograph shows every frame.
|
|
87
|
+
const BLINK_DUR = [0.19, 0.23];
|
|
80
88
|
|
|
81
89
|
// How far into the current gap an odds-rolled evoked blink may land, as a
|
|
82
90
|
// fraction of that gap. A state that moves its gaze oftener than it blinks —
|
|
@@ -226,7 +234,7 @@ export class IdleLayer {
|
|
|
226
234
|
this.talk = 0;
|
|
227
235
|
this._nextBlink = 2 + Math.random() * 3;
|
|
228
236
|
this._blinkT = -1;
|
|
229
|
-
this._blinkDur = 0
|
|
237
|
+
this._blinkDur = BLINK_DUR[0];
|
|
230
238
|
this._lastBlinkAt = -Infinity;
|
|
231
239
|
// Two incommensurate frequencies per axis so the sway never visibly loops.
|
|
232
240
|
this._ph = [Math.random() * 9, Math.random() * 9, Math.random() * 9];
|
|
@@ -318,7 +326,7 @@ export class IdleLayer {
|
|
|
318
326
|
const gap = this._nextBlink - this._lastBlinkAt;
|
|
319
327
|
if (!forced && this.t - this._lastBlinkAt < EVOKED_EARLIEST * gap) return;
|
|
320
328
|
}
|
|
321
|
-
this._startBlink(
|
|
329
|
+
this._startBlink(rand(BLINK_DUR));
|
|
322
330
|
}
|
|
323
331
|
|
|
324
332
|
_startBlink(dur) {
|
package/src/prosody.js
CHANGED
|
@@ -132,7 +132,7 @@ const POSE = {
|
|
|
132
132
|
// and a knit below it — so a phrase sitting negative is the only route a
|
|
133
133
|
// real furrow has into a sentence. Pinned at zero, half the asset was
|
|
134
134
|
// unreachable by construction. The floor is `thoughtful`'s own -0.14, which
|
|
135
|
-
//
|
|
135
|
+
// character-rig's knit weight already cites as a third of the crease; the ceiling
|
|
136
136
|
// stays well under a beat's 0.34 so a held level is never read as an accent.
|
|
137
137
|
brow: [-0.14, 0.18],
|
|
138
138
|
browTauS: 0.8,
|
|
@@ -244,8 +244,7 @@ const BEAT = {
|
|
|
244
244
|
// A diagonal nod's yaw, on the turn's side. Raised with the swing above, and
|
|
245
245
|
// for the same reason — a diagonal beat moves the head across the sentence
|
|
246
246
|
// where a pure pitch nod only moves it down and back. The amplitude is
|
|
247
|
-
// untouched: the nod's read defect was its geometry, never its size
|
|
248
|
-
// (3d-avatar-motion-audit.md, 2026-09-14).
|
|
247
|
+
// untouched: the nod's read defect was its geometry, never its size.
|
|
249
248
|
yawP: 0.45,
|
|
250
249
|
yaw: 0.12,
|
|
251
250
|
};
|
|
@@ -439,6 +438,11 @@ export class SpeechProsody {
|
|
|
439
438
|
* The avatar has entered LISTENING: the floor is the user's. A warmth
|
|
440
439
|
* episode, unless something smiled recently — see `LISTEN_SMILE_REST_MS`,
|
|
441
440
|
* which is what stands between this and a face that is always smiling.
|
|
441
|
+
*
|
|
442
|
+
* Nothing smiles when the user *starts* talking, on purpose. The client has
|
|
443
|
+
* no event for that moment, and a smile the renderer timed for itself would
|
|
444
|
+
* be an acknowledgement nobody sent. The lever for more warmth while
|
|
445
|
+
* listening is the server sending more `ACK_NOD`s, never a timer here.
|
|
442
446
|
*/
|
|
443
447
|
listen() {
|
|
444
448
|
if (this._ms - this._lastWarm < LISTEN_SMILE_REST_MS) return;
|
package/src/speech-timing.js
CHANGED
|
@@ -21,3 +21,31 @@ export const INTERJECTION_TRACK_TAIL_MS = 60;
|
|
|
21
21
|
/** First-order response constants: lips settle before the jaw does. */
|
|
22
22
|
export const MOUTH_RESPONSE_TAU_S = 0.042;
|
|
23
23
|
export const JAW_RESPONSE_TAU_S = 0.07;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* How far past touching a bilabial aims the lips, in `mouthOpen`.
|
|
27
|
+
*
|
|
28
|
+
* Lips do not ease into a closure; they arrive near their peak speed and
|
|
29
|
+
* compress, because the target they are sent to is a *negative* aperture —
|
|
30
|
+
* the lips would pass through each other if they could (Löfqvist & Gracco
|
|
31
|
+
* 1997, JSLHR 40:877). A first-order chase aimed at zero never touches: it
|
|
32
|
+
* slows to nothing on the way in, and a short [b] in running speech ends with
|
|
33
|
+
* the lips still apart and the teeth showing through the gap. Aimed past
|
|
34
|
+
* zero, the chase crosses it at speed and the drawn aperture stops at shut.
|
|
35
|
+
*
|
|
36
|
+
* The literature gives the direction, not a size in these units. This is the
|
|
37
|
+
* knee measured on the Studio lipsync takes (both engines, the mouth modelled
|
|
38
|
+
* at `MOUTH_RESPONSE_TAU_S`): at 0.1 some bilabials still stopped short, at
|
|
39
|
+
* 0.2 all but a stray one met, and 0.3 met no more. From an ordinary open
|
|
40
|
+
* vowel it touches in τ·ln((0.44 + 0.2) / 0.2) ≈ 49 ms.
|
|
41
|
+
*/
|
|
42
|
+
export const LIP_CONTACT_AIM = 0.2;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* How far ahead of a rounded vowel the lips begin to round. Anticipatory
|
|
46
|
+
* rounding is among the longest-range coarticulation there is — 100–300 ms
|
|
47
|
+
* before an audible [y]/[u] (Schwartz & Savariaux 2014) — and this is the
|
|
48
|
+
* bottom of that range, because running speech compresses it and a pucker
|
|
49
|
+
* spread across a whole preceding word reads as a pout.
|
|
50
|
+
*/
|
|
51
|
+
export const ROUND_LEAD_MS = 100;
|
package/src/visemes.js
CHANGED
|
@@ -36,19 +36,50 @@
|
|
|
36
36
|
import {
|
|
37
37
|
CUE_TRACK_LEAD_MS,
|
|
38
38
|
MIN_VISIBLE_CUE_MS,
|
|
39
|
+
ROUND_LEAD_MS,
|
|
39
40
|
SPEECH_TRACK_TAIL_MS,
|
|
40
41
|
} from './speech-timing.js';
|
|
41
42
|
|
|
43
|
+
/**
|
|
44
|
+
* The mouth every face owes the server, at the loudest a cue can say (`i` 1).
|
|
45
|
+
*
|
|
46
|
+
* **The apertures are measured speech, not taste.** The anchor is the open
|
|
47
|
+
* vowel: Huber & Chandrasekaran 2006 (JSLHR 49:1368, Table 2; Optotrak, 30
|
|
48
|
+
* adults, a sentence read aloud) open "Bobby"'s [ɑ] by 7.84 mm of lower lip
|
|
49
|
+
* over the jaw, 7.32 of jaw and 2.49 of upper lip — 17.65 mm of inter-lip
|
|
50
|
+
* opening, pooled over one comfortable and three loud conditions. Their Table 1
|
|
51
|
+
* puts comfortable at 0.83 of that pool (4.86 mm against a loud 6.06–6.35), so
|
|
52
|
+
* ordinary speech opens an [ɑ] **14.6 mm**, and `D` is sized so that it does at
|
|
53
|
+
* `i` 0.5 — which is where PyGato puts an engine's ordinary speaking level
|
|
54
|
+
* (`pygato.lipsync.intensity`). The same paper is the check on the parts this
|
|
55
|
+
* does not set: the jaw's share comes out at 6.6 mm against their ~6.1, and the
|
|
56
|
+
* upper lip at 1.7 mm against ~2.1 — the upper lip is the stable one, a third
|
|
57
|
+
* of the lower's travel, and the rig already had it so.
|
|
58
|
+
*
|
|
59
|
+
* Millimetres are face units at **176.7 mm** a unit (chin to hairline): Lin &
|
|
60
|
+
* Chen 2017 (PLoS ONE 12:e0188638) give women's menton–sellion as 107.8 mm,
|
|
61
|
+
* and sellion sits at 0.61 of the unit on every character here. Their mouth
|
|
62
|
+
* width (44.9 mm) would say 157 — these faces are drawn with slightly wide
|
|
63
|
+
* mouths, and the vertical measure is the one an aperture is.
|
|
64
|
+
*
|
|
65
|
+
* No source reachable gave per-vowel apertures in millimetres, so the other
|
|
66
|
+
* letters keep the ratios to `D` they were authored with and are scaled by the
|
|
67
|
+
* same factor (0.617 of their excursion from rest). Upper-teeth show is part
|
|
68
|
+
* of the opening and scales with it — except on `G`, where teeth on the lip
|
|
69
|
+
* *is* the shape. A mouth this size is what "the mouth is moving a lot" was
|
|
70
|
+
* asking for: the table it replaced opened an ordinary [ɑ] 26 mm, as far as the
|
|
71
|
+
* loud speech in that study opens a shout.
|
|
72
|
+
*/
|
|
42
73
|
export const VISEME_SHAPES = {
|
|
43
74
|
X: { mouthOpen: 0.02, mouthWidth: 0.42, mouthRound: 0.10, mouthPress: 0.15, mouthTuck: 0, teethUpper: 0.00, tongue: 0.0 },
|
|
44
75
|
A: { mouthOpen: 0.00, mouthWidth: 0.40, mouthRound: 0.18, mouthPress: 0.55, mouthTuck: 0, teethUpper: 0.00, tongue: 0.0 },
|
|
45
|
-
B: { mouthOpen: 0.
|
|
46
|
-
C: { mouthOpen: 0.
|
|
47
|
-
D: { mouthOpen: 0.
|
|
48
|
-
E: { mouthOpen: 0.
|
|
49
|
-
F: { mouthOpen: 0.
|
|
50
|
-
G: { mouthOpen: 0.
|
|
51
|
-
H: { mouthOpen: 0.
|
|
76
|
+
B: { mouthOpen: 0.11, mouthWidth: 0.54, mouthRound: 0.05, mouthPress: 0.10, mouthTuck: 0, teethUpper: 0.46, tongue: 0.0 },
|
|
77
|
+
C: { mouthOpen: 0.29, mouthWidth: 0.58, mouthRound: 0.05, mouthPress: 0.00, mouthTuck: 0, teethUpper: 0.28, tongue: 0.0 },
|
|
78
|
+
D: { mouthOpen: 0.53, mouthWidth: 0.52, mouthRound: 0.02, mouthPress: 0.00, mouthTuck: 0, teethUpper: 0.15, tongue: 0.15 },
|
|
79
|
+
E: { mouthOpen: 0.22, mouthWidth: 0.28, mouthRound: 0.55, mouthPress: 0.00, mouthTuck: 0, teethUpper: 0.09, tongue: 0.0 },
|
|
80
|
+
F: { mouthOpen: 0.14, mouthWidth: 0.10, mouthRound: 0.95, mouthPress: 0.10, mouthTuck: 0, teethUpper: 0.00, tongue: 0.0 },
|
|
81
|
+
G: { mouthOpen: 0.13, mouthWidth: 0.46, mouthRound: 0.10, mouthPress: 0.40, mouthTuck: 1.00, teethUpper: 1.00, tongue: 0.0 },
|
|
82
|
+
H: { mouthOpen: 0.25, mouthWidth: 0.48, mouthRound: 0.05, mouthPress: 0.00, mouthTuck: 0, teethUpper: 0.22, tongue: 0.90 },
|
|
52
83
|
};
|
|
53
84
|
|
|
54
85
|
export const VISEME_LETTERS = Object.keys(VISEME_SHAPES);
|
|
@@ -56,6 +87,40 @@ export const VISEME_LETTERS = Object.keys(VISEME_SHAPES);
|
|
|
56
87
|
/** Resting mouth used when nothing is speaking. */
|
|
57
88
|
export const SILENT = 'X';
|
|
58
89
|
|
|
90
|
+
/** The shapes that are a contact, not an aperture: lips together, lip to teeth. */
|
|
91
|
+
const CLOSURES = new Set(['A', 'G']);
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* How far the mandible falls for a given aperture — `jaw` is "follows
|
|
95
|
+
* mouthOpen but slower" (`params.js`) and this is the slower.
|
|
96
|
+
*
|
|
97
|
+
* It is exported because it is not a preference: **every amplitude behind the
|
|
98
|
+
* lips was calibrated at this ratio**, so a driver that picks its own moves the
|
|
99
|
+
* mandible relative to the lip that hides it. The lower arch is the surface
|
|
100
|
+
* that shows it, because it rides `jaw` alone while the lip peels off it on
|
|
101
|
+
* `mouthOpen`: what is seen of the arch is the difference between two much
|
|
102
|
+
* larger travels (`avatar-3d/scripts/morphs.ARCH_DROP`), so it goes to nothing
|
|
103
|
+
* at a ratio only a little above this one. A face driven at 1:1 opens wide and
|
|
104
|
+
* shows no lower teeth at any aperture — the arch descends as fast as the lip
|
|
105
|
+
* uncovering it, which is what mocap did until it read this number instead of
|
|
106
|
+
* ARKit's `jawOpen` directly.
|
|
107
|
+
*/
|
|
108
|
+
export const JAW_OF_OPEN = 0.7;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* How much of a shape's excursion a cue's loudness buys, as `k = LOUDNESS_FLOOR
|
|
112
|
+
* + (1 - LOUDNESS_FLOOR) * i`.
|
|
113
|
+
*
|
|
114
|
+
* Louder speech is the same movement made bigger, and not by much: +10 dB
|
|
115
|
+
* opens the lips and jaw 1.28x as far (Huber & Chandrasekaran 2006, Table 1:
|
|
116
|
+
* 4.86 mm comfortable, 6.06–6.35 loud; Schulman 1989 calls it "amplification
|
|
117
|
+
* of normal movement patterns"). `i` spans 16 dB with ordinary speech at 0.5,
|
|
118
|
+
* so 0.5 either side is 8 dB and 1.22x — k runs 0.776 : 1 : 1.224 across it,
|
|
119
|
+
* which normalised to the loud end is this floor. The quiet side is the loud
|
|
120
|
+
* side's slope extended; nothing measured it.
|
|
121
|
+
*/
|
|
122
|
+
const LOUDNESS_FLOOR = 0.634;
|
|
123
|
+
|
|
59
124
|
/**
|
|
60
125
|
* Scale a shape by loudness. Only the "effortful" channels scale — a quiet 'D'
|
|
61
126
|
* is a small D, not a different shape.
|
|
@@ -63,7 +128,7 @@ export const SILENT = 'X';
|
|
|
63
128
|
export function shapeFor(letter, intensity = 1) {
|
|
64
129
|
const base = VISEME_SHAPES[letter] || VISEME_SHAPES[SILENT];
|
|
65
130
|
const rest = VISEME_SHAPES.X;
|
|
66
|
-
const k =
|
|
131
|
+
const k = LOUDNESS_FLOOR + (1 - LOUDNESS_FLOOR) * Math.max(0, Math.min(1, intensity));
|
|
67
132
|
return {
|
|
68
133
|
mouthOpen: rest.mouthOpen + (base.mouthOpen - rest.mouthOpen) * k,
|
|
69
134
|
mouthWidth: rest.mouthWidth + (base.mouthWidth - rest.mouthWidth) * k,
|
|
@@ -72,7 +137,7 @@ export function shapeFor(letter, intensity = 1) {
|
|
|
72
137
|
mouthTuck: base.mouthTuck,
|
|
73
138
|
teethUpper: base.teethUpper * k,
|
|
74
139
|
tongue: base.tongue,
|
|
75
|
-
jaw: (rest.mouthOpen + (base.mouthOpen - rest.mouthOpen) * k) *
|
|
140
|
+
jaw: (rest.mouthOpen + (base.mouthOpen - rest.mouthOpen) * k) * JAW_OF_OPEN,
|
|
76
141
|
};
|
|
77
142
|
}
|
|
78
143
|
|
|
@@ -127,7 +192,7 @@ export function normalizeCues(cues) {
|
|
|
127
192
|
if (prev && head.t - prev.t < MIN_VISIBLE_CUE_MS) {
|
|
128
193
|
// Too short to read. Keep whichever is more visually salient: a closure
|
|
129
194
|
// (A/G) carries more lip-reading information than a mid-open vowel.
|
|
130
|
-
if (v
|
|
195
|
+
if (CLOSURES.has(v)) {
|
|
131
196
|
// The short cue can sit between two identical closures (G → F → G).
|
|
132
197
|
// Its replacement would otherwise create a duplicate visible shape;
|
|
133
198
|
// preserving the first G is both the stable wire form and the face the
|
|
@@ -209,6 +274,14 @@ export class VisemeTrack {
|
|
|
209
274
|
this.cues = merged;
|
|
210
275
|
// Re-seek rather than trusting the old index against a re-normalized array.
|
|
211
276
|
this._idx = 0;
|
|
277
|
+
// A sentence can arrive after the one before it has played out — a TTS
|
|
278
|
+
// whose next sentence lags its own audio does this routinely — and the
|
|
279
|
+
// track had stopped at that sentence's tail. Resume on the same clock, or
|
|
280
|
+
// the face holds still through audio the user can hear.
|
|
281
|
+
if (!this.playing && this.clock && merged.length) {
|
|
282
|
+
const last = merged[merged.length - 1];
|
|
283
|
+
this.playing = last.v !== SILENT || this.clock() + LEAD_MS <= last.t + this.tailMs;
|
|
284
|
+
}
|
|
212
285
|
}
|
|
213
286
|
|
|
214
287
|
stop() {
|
|
@@ -218,7 +291,19 @@ export class VisemeTrack {
|
|
|
218
291
|
this._idx = 0;
|
|
219
292
|
}
|
|
220
293
|
|
|
221
|
-
/**
|
|
294
|
+
/**
|
|
295
|
+
* The shape to aim at now, which is not always the cue under the clock.
|
|
296
|
+
*
|
|
297
|
+
* Rounding starts before its sound, because it is the kind of gesture a
|
|
298
|
+
* viewer reads a word by (speech-timing.js has the measurements): `round`
|
|
299
|
+
* carries the pucker of a rounded vowel coming up within `ROUND_LEAD_MS`, for
|
|
300
|
+
* the mixer to hold against the current shape's own. It does not reach
|
|
301
|
+
* across a rest. A closure's early onset is not drawn here — the server
|
|
302
|
+
* already starts every closure cue ahead of its phone, and a second lead on
|
|
303
|
+
* top of that closed the lips a whole vowel early.
|
|
304
|
+
*
|
|
305
|
+
* @returns {{letter: string, intensity: number, phone: string | null, round: number} | null}
|
|
306
|
+
*/
|
|
222
307
|
sample() {
|
|
223
308
|
if (!this.playing || !this.cues.length || !this.clock) return null;
|
|
224
309
|
const now = this.clock() + LEAD_MS;
|
|
@@ -237,8 +322,17 @@ export class VisemeTrack {
|
|
|
237
322
|
}
|
|
238
323
|
|
|
239
324
|
const cue = this.cues[this._idx];
|
|
240
|
-
if (cue.t > now) return { letter: SILENT, intensity: 1, phone: null };
|
|
241
|
-
|
|
325
|
+
if (cue.t > now) return { letter: SILENT, intensity: 1, phone: null, round: 0 };
|
|
326
|
+
let round = 0;
|
|
327
|
+
for (let j = this._idx + 1; j < this.cues.length; j++) {
|
|
328
|
+
const ahead = this.cues[j];
|
|
329
|
+
if (ahead.v === SILENT || ahead.t - now > ROUND_LEAD_MS) break;
|
|
330
|
+
// Across consonants too: protrusion before a rounded vowel grows with the
|
|
331
|
+
// consonants in front of it (Noiray et al. 2011, JASA 129:340), so the
|
|
332
|
+
// lips of "sm-" in "smooth" are already on their way.
|
|
333
|
+
round = Math.max(round, VISEME_SHAPES[ahead.v].mouthRound);
|
|
334
|
+
}
|
|
335
|
+
return { letter: cue.v, intensity: cue.i == null ? 1 : cue.i, phone: cue.p ?? null, round };
|
|
242
336
|
}
|
|
243
337
|
}
|
|
244
338
|
|
package/client/arjun.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Professional Indian male interviewer, authored to read at call-tile size. */
|
|
2
|
-
|
|
3
|
-
import { createCanvasAvatar } from './createCanvasAvatar.js';
|
|
4
|
-
import type { CanvasAvatarOptions } from './createCanvasAvatar.js';
|
|
5
|
-
import type { AvatarInstance } from './createAvatar.js';
|
|
6
|
-
|
|
7
|
-
const RIG_URL = new URL('../src/canvas/data/interviewer-male.rig.json', import.meta.url);
|
|
8
|
-
const IMAGES = {
|
|
9
|
-
'round-m3-hair-back.webp': new URL('../src/canvas/data/img/round-m3-hair-back.webp', import.meta.url),
|
|
10
|
-
'round-m3-top-body.webp': new URL('../src/canvas/data/img/round-m3-top-body.webp', import.meta.url),
|
|
11
|
-
'round-m3-hair-front.webp': new URL('../src/canvas/data/img/round-m3-hair-front.webp', import.meta.url),
|
|
12
|
-
} as const;
|
|
13
|
-
const FACE = () => import('../src/canvas/avatars/round/face.mjs');
|
|
14
|
-
|
|
15
|
-
export type { CanvasAvatarOptions };
|
|
16
|
-
// Same mixer, same clip library, so the same declaration (`supports.ts`).
|
|
17
|
-
export { supports } from './supports.js';
|
|
18
|
-
|
|
19
|
-
export function createAvatar(options: CanvasAvatarOptions): AvatarInstance {
|
|
20
|
-
return createCanvasAvatar(options, {
|
|
21
|
-
rigUrl: RIG_URL,
|
|
22
|
-
images: IMAGES,
|
|
23
|
-
face: FACE,
|
|
24
|
-
label: 'Arjun — professional male interviewer avatar',
|
|
25
|
-
});
|
|
26
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/** Shared private constructor for the code-authored canvas avatars. */
|
|
2
|
-
|
|
3
|
-
import type { PipecatClient } from '@pipecat-ai/client-js';
|
|
4
|
-
import { createAvatar as createMixer, STATES } from '../src/avatar.js';
|
|
5
|
-
import type { Gain } from '../src/avatar.js';
|
|
6
|
-
import { createCanvasRig } from '../src/canvas/create-rig.js';
|
|
7
|
-
import { AvatarClient } from './AvatarClient.js';
|
|
8
|
-
import type { AvatarInstance, AvatarOptions } from './createAvatar.js';
|
|
9
|
-
|
|
10
|
-
export interface CanvasAvatarOptions extends AvatarOptions {
|
|
11
|
-
/** Viseme amplitude, 0..2. `1` is as authored. */
|
|
12
|
-
readonly mouthGain?: Gain;
|
|
13
|
-
/** Gesture-clip amplitude, 0..2. */
|
|
14
|
-
readonly gestureGain?: Gain;
|
|
15
|
-
/** Idle/liveness amplitude, 0..2. */
|
|
16
|
-
readonly motionGain?: Gain;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/** @deprecated Use `CanvasAvatarOptions`; kept for the original entry points. */
|
|
20
|
-
export type InterviewerAvatarOptions = CanvasAvatarOptions;
|
|
21
|
-
|
|
22
|
-
interface CanvasAvatarConfig {
|
|
23
|
-
readonly rigUrl: URL;
|
|
24
|
-
readonly images: Readonly<Record<string, URL>>;
|
|
25
|
-
readonly face: () => Promise<unknown>;
|
|
26
|
-
readonly label: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const GAINS = ['mouthGain', 'gestureGain', 'motionGain'] as const;
|
|
30
|
-
const PRESENCE_FILTERS: Readonly<Record<string, string>> = {
|
|
31
|
-
DEGRADED: String(STATES.DEGRADED.filter ?? ''),
|
|
32
|
-
OFFLINE: String(STATES.OFFLINE.filter ?? ''),
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export function createCanvasAvatar(
|
|
36
|
-
options: CanvasAvatarOptions,
|
|
37
|
-
config: CanvasAvatarConfig,
|
|
38
|
-
): AvatarInstance {
|
|
39
|
-
const { mount, client, ...gains } = options;
|
|
40
|
-
if (!mount) throw new TypeError('createAvatar: `mount` is required');
|
|
41
|
-
if (!client) throw new TypeError('createAvatar: `client` is required');
|
|
42
|
-
for (const key of GAINS) {
|
|
43
|
-
const gain = gains[key];
|
|
44
|
-
if (gain === undefined) continue;
|
|
45
|
-
if (!Number.isFinite(gain) || gain < 0 || gain > 2) {
|
|
46
|
-
throw new RangeError(`createAvatar: \`${key}\` must be 0..2, got ${String(gain)}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const canvas = createCanvasRig({
|
|
51
|
-
url: config.rigUrl,
|
|
52
|
-
images: config.images,
|
|
53
|
-
face: config.face,
|
|
54
|
-
label: config.label,
|
|
55
|
-
});
|
|
56
|
-
const widget = createMixer({ mount, rig: canvas.factory, ...gains });
|
|
57
|
-
const driver = new AvatarClient(widget, {
|
|
58
|
-
onPresenceChange: (state) => canvas.setPresenceFilter(PRESENCE_FILTERS[state] ?? ''),
|
|
59
|
-
});
|
|
60
|
-
const detach = driver.attach(client as PipecatClient);
|
|
61
|
-
|
|
62
|
-
let destroyed = false;
|
|
63
|
-
return {
|
|
64
|
-
destroy() {
|
|
65
|
-
if (destroyed) return;
|
|
66
|
-
destroyed = true;
|
|
67
|
-
detach();
|
|
68
|
-
driver.destroy();
|
|
69
|
-
widget.destroy();
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
}
|
package/client/ishita.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Polished professional female avatar, authored to read at call-tile size. */
|
|
2
|
-
|
|
3
|
-
import { createCanvasAvatar } from './createCanvasAvatar.js';
|
|
4
|
-
import type { CanvasAvatarOptions } from './createCanvasAvatar.js';
|
|
5
|
-
import type { AvatarInstance } from './createAvatar.js';
|
|
6
|
-
|
|
7
|
-
const RIG_URL = new URL('../src/canvas/data/professional-female-a.rig.json', import.meta.url);
|
|
8
|
-
const IMAGES = {
|
|
9
|
-
'professional-female-a-top-body.webp': new URL('../src/canvas/data/img/professional-female-a-top-body.webp', import.meta.url),
|
|
10
|
-
'professional-female-a-hair-back.webp': new URL('../src/canvas/data/img/professional-female-a-hair-back.webp', import.meta.url),
|
|
11
|
-
'professional-female-a-hair-front.webp': new URL('../src/canvas/data/img/professional-female-a-hair-front.webp', import.meta.url),
|
|
12
|
-
} as const;
|
|
13
|
-
const FACE = () => import('../src/canvas/avatars/round/face.mjs');
|
|
14
|
-
|
|
15
|
-
export type { CanvasAvatarOptions };
|
|
16
|
-
// Same mixer, same clip library, so the same declaration (`supports.ts`).
|
|
17
|
-
export { supports } from './supports.js';
|
|
18
|
-
|
|
19
|
-
export function createAvatar(options: CanvasAvatarOptions): AvatarInstance {
|
|
20
|
-
return createCanvasAvatar(options, {
|
|
21
|
-
rigUrl: RIG_URL,
|
|
22
|
-
images: IMAGES,
|
|
23
|
-
face: FACE,
|
|
24
|
-
label: 'Ishita — polished professional female avatar',
|
|
25
|
-
});
|
|
26
|
-
}
|
package/client/kabir.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Relaxed professional male avatar, authored to read at call-tile size. */
|
|
2
|
-
|
|
3
|
-
import { createCanvasAvatar } from './createCanvasAvatar.js';
|
|
4
|
-
import type { CanvasAvatarOptions } from './createCanvasAvatar.js';
|
|
5
|
-
import type { AvatarInstance } from './createAvatar.js';
|
|
6
|
-
|
|
7
|
-
const RIG_URL = new URL('../src/canvas/data/professional-male-b.rig.json', import.meta.url);
|
|
8
|
-
const IMAGES = {
|
|
9
|
-
'professional-male-b-top-body.webp': new URL('../src/canvas/data/img/professional-male-b-top-body.webp', import.meta.url),
|
|
10
|
-
'professional-male-b-hair-back.webp': new URL('../src/canvas/data/img/professional-male-b-hair-back.webp', import.meta.url),
|
|
11
|
-
'professional-male-b-hair-front.webp': new URL('../src/canvas/data/img/professional-male-b-hair-front.webp', import.meta.url),
|
|
12
|
-
} as const;
|
|
13
|
-
const FACE = () => import('../src/canvas/avatars/round/face.mjs');
|
|
14
|
-
|
|
15
|
-
export type { CanvasAvatarOptions };
|
|
16
|
-
// Same mixer, same clip library, so the same declaration (`supports.ts`).
|
|
17
|
-
export { supports } from './supports.js';
|
|
18
|
-
|
|
19
|
-
export function createAvatar(options: CanvasAvatarOptions): AvatarInstance {
|
|
20
|
-
return createCanvasAvatar(options, {
|
|
21
|
-
rigUrl: RIG_URL,
|
|
22
|
-
images: IMAGES,
|
|
23
|
-
face: FACE,
|
|
24
|
-
label: 'Kabir — relaxed professional male avatar',
|
|
25
|
-
});
|
|
26
|
-
}
|
package/client/meera.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Professional Indian female interviewer, authored to read at call-tile size. */
|
|
2
|
-
|
|
3
|
-
import { createCanvasAvatar } from './createCanvasAvatar.js';
|
|
4
|
-
import type { CanvasAvatarOptions } from './createCanvasAvatar.js';
|
|
5
|
-
import type { AvatarInstance } from './createAvatar.js';
|
|
6
|
-
|
|
7
|
-
const RIG_URL = new URL('../src/canvas/data/interviewer-female.rig.json', import.meta.url);
|
|
8
|
-
const IMAGES = {
|
|
9
|
-
'round-w1-hair-back.webp': new URL('../src/canvas/data/img/round-w1-hair-back.webp', import.meta.url),
|
|
10
|
-
'round-w1-top-body.webp': new URL('../src/canvas/data/img/round-w1-top-body.webp', import.meta.url),
|
|
11
|
-
'round-w1-hair-front.webp': new URL('../src/canvas/data/img/round-w1-hair-front.webp', import.meta.url),
|
|
12
|
-
} as const;
|
|
13
|
-
const FACE = () => import('../src/canvas/avatars/round/face.mjs');
|
|
14
|
-
|
|
15
|
-
export type { CanvasAvatarOptions };
|
|
16
|
-
// Same mixer, same clip library, so the same declaration (`supports.ts`).
|
|
17
|
-
export { supports } from './supports.js';
|
|
18
|
-
|
|
19
|
-
export function createAvatar(options: CanvasAvatarOptions): AvatarInstance {
|
|
20
|
-
return createCanvasAvatar(options, {
|
|
21
|
-
rigUrl: RIG_URL,
|
|
22
|
-
images: IMAGES,
|
|
23
|
-
face: FACE,
|
|
24
|
-
label: 'Meera — professional female interviewer avatar',
|
|
25
|
-
});
|
|
26
|
-
}
|
package/client/naina.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Relaxed professional female avatar, authored to read at call-tile size. */
|
|
2
|
-
|
|
3
|
-
import { createCanvasAvatar } from './createCanvasAvatar.js';
|
|
4
|
-
import type { CanvasAvatarOptions } from './createCanvasAvatar.js';
|
|
5
|
-
import type { AvatarInstance } from './createAvatar.js';
|
|
6
|
-
|
|
7
|
-
const RIG_URL = new URL('../src/canvas/data/professional-female-b.rig.json', import.meta.url);
|
|
8
|
-
const IMAGES = {
|
|
9
|
-
'professional-female-b-top-body.webp': new URL('../src/canvas/data/img/professional-female-b-top-body.webp', import.meta.url),
|
|
10
|
-
'professional-female-b-hair-back.webp': new URL('../src/canvas/data/img/professional-female-b-hair-back.webp', import.meta.url),
|
|
11
|
-
'professional-female-b-hair-front.webp': new URL('../src/canvas/data/img/professional-female-b-hair-front.webp', import.meta.url),
|
|
12
|
-
} as const;
|
|
13
|
-
const FACE = () => import('../src/canvas/avatars/round/face.mjs');
|
|
14
|
-
|
|
15
|
-
export type { CanvasAvatarOptions };
|
|
16
|
-
// Same mixer, same clip library, so the same declaration (`supports.ts`).
|
|
17
|
-
export { supports } from './supports.js';
|
|
18
|
-
|
|
19
|
-
export function createAvatar(options: CanvasAvatarOptions): AvatarInstance {
|
|
20
|
-
return createCanvasAvatar(options, {
|
|
21
|
-
rigUrl: RIG_URL,
|
|
22
|
-
images: IMAGES,
|
|
23
|
-
face: FACE,
|
|
24
|
-
label: 'Naina — relaxed professional female avatar',
|
|
25
|
-
});
|
|
26
|
-
}
|
package/client/vikram.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/** Polished professional male avatar, authored to read at call-tile size. */
|
|
2
|
-
|
|
3
|
-
import { createCanvasAvatar } from './createCanvasAvatar.js';
|
|
4
|
-
import type { CanvasAvatarOptions } from './createCanvasAvatar.js';
|
|
5
|
-
import type { AvatarInstance } from './createAvatar.js';
|
|
6
|
-
|
|
7
|
-
const RIG_URL = new URL('../src/canvas/data/professional-male-a.rig.json', import.meta.url);
|
|
8
|
-
const IMAGES = {
|
|
9
|
-
'professional-male-a-top-body.webp': new URL('../src/canvas/data/img/professional-male-a-top-body.webp', import.meta.url),
|
|
10
|
-
'professional-male-a-hair-back.webp': new URL('../src/canvas/data/img/professional-male-a-hair-back.webp', import.meta.url),
|
|
11
|
-
'professional-male-a-hair-front.webp': new URL('../src/canvas/data/img/professional-male-a-hair-front.webp', import.meta.url),
|
|
12
|
-
} as const;
|
|
13
|
-
const FACE = () => import('../src/canvas/avatars/round/face.mjs');
|
|
14
|
-
|
|
15
|
-
export type { CanvasAvatarOptions };
|
|
16
|
-
// Same mixer, same clip library, so the same declaration (`supports.ts`).
|
|
17
|
-
export { supports } from './supports.js';
|
|
18
|
-
|
|
19
|
-
export function createAvatar(options: CanvasAvatarOptions): AvatarInstance {
|
|
20
|
-
return createCanvasAvatar(options, {
|
|
21
|
-
rigUrl: RIG_URL,
|
|
22
|
-
images: IMAGES,
|
|
23
|
-
face: FACE,
|
|
24
|
-
label: 'Vikram — polished professional male avatar',
|
|
25
|
-
});
|
|
26
|
-
}
|
package/dist/arjun.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/** Professional Indian male interviewer, authored to read at call-tile size. */
|
|
2
|
-
import type { CanvasAvatarOptions } from './createCanvasAvatar.js';
|
|
3
|
-
import type { AvatarInstance } from './createAvatar.js';
|
|
4
|
-
export type { CanvasAvatarOptions };
|
|
5
|
-
export { supports } from './supports.js';
|
|
6
|
-
export declare function createAvatar(options: CanvasAvatarOptions): AvatarInstance;
|
|
7
|
-
//# sourceMappingURL=arjun.d.ts.map
|
package/dist/arjun.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"arjun.d.ts","sourceRoot":"","sources":["../client/arjun.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAGhF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAUxD,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,cAAc,CAOzE"}
|
package/dist/arjun.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/** Professional Indian male interviewer, authored to read at call-tile size. */
|
|
2
|
-
import { createCanvasAvatar } from './createCanvasAvatar.js';
|
|
3
|
-
const RIG_URL = new URL('../src/canvas/data/interviewer-male.rig.json', import.meta.url);
|
|
4
|
-
const IMAGES = {
|
|
5
|
-
'round-m3-hair-back.webp': new URL('../src/canvas/data/img/round-m3-hair-back.webp', import.meta.url),
|
|
6
|
-
'round-m3-top-body.webp': new URL('../src/canvas/data/img/round-m3-top-body.webp', import.meta.url),
|
|
7
|
-
'round-m3-hair-front.webp': new URL('../src/canvas/data/img/round-m3-hair-front.webp', import.meta.url),
|
|
8
|
-
};
|
|
9
|
-
const FACE = () => import('../src/canvas/avatars/round/face.mjs');
|
|
10
|
-
// Same mixer, same clip library, so the same declaration (`supports.ts`).
|
|
11
|
-
export { supports } from './supports.js';
|
|
12
|
-
export function createAvatar(options) {
|
|
13
|
-
return createCanvasAvatar(options, {
|
|
14
|
-
rigUrl: RIG_URL,
|
|
15
|
-
images: IMAGES,
|
|
16
|
-
face: FACE,
|
|
17
|
-
label: 'Arjun — professional male interviewer avatar',
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
//# sourceMappingURL=arjun.js.map
|
package/dist/arjun.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"arjun.js","sourceRoot":"","sources":["../client/arjun.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAI7D,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,8CAA8C,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzF,MAAM,MAAM,GAAG;IACb,yBAAyB,EAAE,IAAI,GAAG,CAAC,gDAAgD,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IACrG,wBAAwB,EAAE,IAAI,GAAG,CAAC,+CAA+C,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IACnG,0BAA0B,EAAE,IAAI,GAAG,CAAC,iDAAiD,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;CAC/F,CAAC;AACX,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,sCAAsC,CAAC,CAAC;AAGlE,0EAA0E;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,OAAO,kBAAkB,CAAC,OAAO,EAAE;QACjC,MAAM,EAAE,OAAO;QACf,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI;QACV,KAAK,EAAE,8CAA8C;KACtD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/** Shared private constructor for the code-authored canvas avatars. */
|
|
2
|
-
import type { Gain } from '../src/avatar.js';
|
|
3
|
-
import type { AvatarInstance, AvatarOptions } from './createAvatar.js';
|
|
4
|
-
export interface CanvasAvatarOptions extends AvatarOptions {
|
|
5
|
-
/** Viseme amplitude, 0..2. `1` is as authored. */
|
|
6
|
-
readonly mouthGain?: Gain;
|
|
7
|
-
/** Gesture-clip amplitude, 0..2. */
|
|
8
|
-
readonly gestureGain?: Gain;
|
|
9
|
-
/** Idle/liveness amplitude, 0..2. */
|
|
10
|
-
readonly motionGain?: Gain;
|
|
11
|
-
}
|
|
12
|
-
/** @deprecated Use `CanvasAvatarOptions`; kept for the original entry points. */
|
|
13
|
-
export type InterviewerAvatarOptions = CanvasAvatarOptions;
|
|
14
|
-
interface CanvasAvatarConfig {
|
|
15
|
-
readonly rigUrl: URL;
|
|
16
|
-
readonly images: Readonly<Record<string, URL>>;
|
|
17
|
-
readonly face: () => Promise<unknown>;
|
|
18
|
-
readonly label: string;
|
|
19
|
-
}
|
|
20
|
-
export declare function createCanvasAvatar(options: CanvasAvatarOptions, config: CanvasAvatarConfig): AvatarInstance;
|
|
21
|
-
export {};
|
|
22
|
-
//# sourceMappingURL=createCanvasAvatar.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createCanvasAvatar.d.ts","sourceRoot":"","sources":["../client/createCanvasAvatar.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAIvE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvE,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,kDAAkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC;IAC1B,oCAAoC;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC;IAC5B,qCAAqC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC;CAC5B;AAED,iFAAiF;AACjF,MAAM,MAAM,wBAAwB,GAAG,mBAAmB,CAAC;AAE3D,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAQD,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,kBAAkB,GACzB,cAAc,CAkChB"}
|