@lovelace_lol/polymer 0.0.1
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 +226 -0
- package/dist/index.d.ts +2603 -0
- package/dist/index.js +1031 -0
- package/dist/test.cjs +1519 -0
- package/dist/worker/agency-worker.js +972 -0
- package/dist/worker/manifest.edn +1 -0
- package/package.json +57 -0
- package/shadow-cljs.edn +173 -0
- package/src/polymer/animation/agency.cljs +84 -0
- package/src/polymer/animation/domain.cljs +110 -0
- package/src/polymer/animation/planner.cljs +88 -0
- package/src/polymer/animation/runtime.cljs +137 -0
- package/src/polymer/animation/scheduler.cljs +178 -0
- package/src/polymer/animation/service.cljs +981 -0
- package/src/polymer/animation/state.cljs +112 -0
- package/src/polymer/blink/agency.cljs +131 -0
- package/src/polymer/blink/planner.cljs +115 -0
- package/src/polymer/blink/scheduler.cljs +90 -0
- package/src/polymer/blink/snippet.cljs +50 -0
- package/src/polymer/blink/state.cljs +102 -0
- package/src/polymer/camera_context/agency.cljs +132 -0
- package/src/polymer/camera_context/domain.cljs +186 -0
- package/src/polymer/camera_context/planner.cljs +50 -0
- package/src/polymer/camera_context/scheduler.cljs +113 -0
- package/src/polymer/camera_context/state.cljs +121 -0
- package/src/polymer/character.cljs +471 -0
- package/src/polymer/conversation/agency.cljs +83 -0
- package/src/polymer/conversation/domain.cljs +35 -0
- package/src/polymer/conversation/effector.cljs +130 -0
- package/src/polymer/conversation/planner.cljs +178 -0
- package/src/polymer/conversation/scheduler.cljs +202 -0
- package/src/polymer/conversation/service.cljs +329 -0
- package/src/polymer/conversation/state.cljs +186 -0
- package/src/polymer/core.cljs +172 -0
- package/src/polymer/eye_head/agency.cljs +75 -0
- package/src/polymer/eye_head/domain.cljs +133 -0
- package/src/polymer/eye_head/goap.cljs +106 -0
- package/src/polymer/eye_head/scheduler.cljs +176 -0
- package/src/polymer/eye_head/service.cljs +259 -0
- package/src/polymer/eye_head/snippet.cljs +92 -0
- package/src/polymer/eye_head/state.cljs +167 -0
- package/src/polymer/gaze/agency.cljs +85 -0
- package/src/polymer/gaze/domain.cljs +211 -0
- package/src/polymer/gaze/goap.cljs +208 -0
- package/src/polymer/gaze/scheduler.cljs +184 -0
- package/src/polymer/gaze/state.cljs +202 -0
- package/src/polymer/gesture/agency.cljs +185 -0
- package/src/polymer/gesture/goap.cljs +461 -0
- package/src/polymer/gesture/scheduler.cljs +122 -0
- package/src/polymer/gesture/snippet.cljs +198 -0
- package/src/polymer/gesture/state.cljs +273 -0
- package/src/polymer/lipsync/agency.cljs +593 -0
- package/src/polymer/lipsync/articulation/jaw.cljs +258 -0
- package/src/polymer/lipsync/articulation/snippet.cljs +605 -0
- package/src/polymer/lipsync/articulation/tongue.cljs +418 -0
- package/src/polymer/lipsync/articulation/visemes.cljs +429 -0
- package/src/polymer/lipsync/goap.cljs +132 -0
- package/src/polymer/lipsync/pronunciation/effector.cljs +51 -0
- package/src/polymer/lipsync/scheduler.cljs +302 -0
- package/src/polymer/lipsync/state.cljs +202 -0
- package/src/polymer/nlp/phonemes.cljs +164 -0
- package/src/polymer/prosodic/agency.cljs +196 -0
- package/src/polymer/prosodic/goap.cljs +122 -0
- package/src/polymer/prosodic/scheduler.cljs +99 -0
- package/src/polymer/prosodic/snippet.cljs +106 -0
- package/src/polymer/prosodic/state.cljs +139 -0
- package/src/polymer/runtime/embody.cljs +117 -0
- package/src/polymer/runtime/embody_wasm.cljs +177 -0
- package/src/polymer/runtime/rust_embody_host.cljs +645 -0
- package/src/polymer/society.cljs +239 -0
- package/src/polymer/stream.cljs +130 -0
- package/src/polymer/transcription/agency.cljs +69 -0
- package/src/polymer/transcription/domain.cljs +68 -0
- package/src/polymer/transcription/effector.cljs +611 -0
- package/src/polymer/transcription/planner.cljs +203 -0
- package/src/polymer/transcription/scheduler.cljs +225 -0
- package/src/polymer/transcription/service.cljs +209 -0
- package/src/polymer/transcription/state.cljs +239 -0
- package/src/polymer/tts/agency.cljs +503 -0
- package/src/polymer/tts/azure.cljs +290 -0
- package/src/polymer/tts/planner.cljs +178 -0
- package/src/polymer/tts/runtime.cljs +624 -0
- package/src/polymer/tts/scheduler.cljs +162 -0
- package/src/polymer/tts/state.cljs +191 -0
- package/src/polymer/worker.cljs +70 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
(ns polymer.lipsync.articulation.jaw
|
|
2
|
+
(:require [clojure.string :as str]
|
|
3
|
+
[polymer.lipsync.state :as state]
|
|
4
|
+
[polymer.lipsync.articulation.visemes :as visemes]))
|
|
5
|
+
|
|
6
|
+
;; The jaw planner is deliberately separate from lip/viseme curve assembly.
|
|
7
|
+
;; JALI-style lip sync treats mouth shape and jaw drop as related but distinct
|
|
8
|
+
;; control axes: a bilabial can close the lips without opening AU26, and a vowel
|
|
9
|
+
;; nucleus can keep the jaw open while the lips travel through a diphthong.
|
|
10
|
+
;;
|
|
11
|
+
;; Inputs are normalized viseme timeline events. Outputs are jaw-activation
|
|
12
|
+
;; keyframes; snippet assembly later routes those keyframes to the JAW bone so
|
|
13
|
+
;; the CC4 Jaw_Open morph does not cover the independent viseme shapes. There are
|
|
14
|
+
;; no host side effects here: no audio, DOM, scheduler, engine handle, UI state,
|
|
15
|
+
;; or provider credential access.
|
|
16
|
+
|
|
17
|
+
(def intensity-eps 0.001)
|
|
18
|
+
(def jaw-long-gap-ms 90)
|
|
19
|
+
(def jaw-group-gap-ms 65)
|
|
20
|
+
(def jaw-transition-lead-ms 24)
|
|
21
|
+
(def jaw-vowel-attack-ms 42)
|
|
22
|
+
(def jaw-vowel-release-ms 45)
|
|
23
|
+
(def jaw-consonant-attack-ms 18)
|
|
24
|
+
(def jaw-consonant-release-ms 28)
|
|
25
|
+
(def jaw-bridge-ratio 0.55)
|
|
26
|
+
|
|
27
|
+
(defn finite-number? [value]
|
|
28
|
+
(and (number? value) (js/isFinite value)))
|
|
29
|
+
|
|
30
|
+
(defn class-name [value]
|
|
31
|
+
(cond
|
|
32
|
+
(keyword? value) (name value)
|
|
33
|
+
(string? value) (str/lower-case value)
|
|
34
|
+
(nil? value) nil
|
|
35
|
+
:else (str/lower-case (str value))))
|
|
36
|
+
|
|
37
|
+
(defn event-classes [event]
|
|
38
|
+
;; Provider timelines can be lossy, so the event may carry both a primary
|
|
39
|
+
;; phoneme class and a full class set. If either is absent, fall back to the
|
|
40
|
+
;; canonical viseme slot class so planner rules still behave conservatively.
|
|
41
|
+
(let [from-event (into []
|
|
42
|
+
(keep class-name)
|
|
43
|
+
(concat (or (:phonemeClasses event) [])
|
|
44
|
+
[(:phonemeClass event)]))
|
|
45
|
+
fallback (into [] (keep class-name) (visemes/viseme-classes (:visemeId event)))]
|
|
46
|
+
(set (if (seq from-event) from-event fallback))))
|
|
47
|
+
|
|
48
|
+
(defn has-class? [event class]
|
|
49
|
+
(contains? (event-classes event) class))
|
|
50
|
+
|
|
51
|
+
(defn raw-target [event]
|
|
52
|
+
(state/clamp 0 2
|
|
53
|
+
(state/number-or (:jawActivation event)
|
|
54
|
+
(visemes/jaw-activation-for-viseme (:visemeId event)))))
|
|
55
|
+
|
|
56
|
+
(defn start-ms [event]
|
|
57
|
+
(state/number-or (:offsetMs event) 0))
|
|
58
|
+
|
|
59
|
+
(defn end-ms [event]
|
|
60
|
+
(+ (start-ms event) (max 1 (state/number-or (:durationMs event) 1))))
|
|
61
|
+
|
|
62
|
+
(defn pause-event? [event]
|
|
63
|
+
(or (has-class? event "pause")
|
|
64
|
+
(str/starts-with? (or (:phoneme event) "") "PAUSE_")))
|
|
65
|
+
|
|
66
|
+
(defn bilabial? [event]
|
|
67
|
+
(has-class? event "bilabial"))
|
|
68
|
+
|
|
69
|
+
(defn narrow-fricative? [event]
|
|
70
|
+
(or (has-class? event "sibilant")
|
|
71
|
+
(has-class? event "labiodental")
|
|
72
|
+
(has-class? event "dental")
|
|
73
|
+
(has-class? event "fricative")))
|
|
74
|
+
|
|
75
|
+
(defn tongue-consonant? [event]
|
|
76
|
+
(or (has-class? event "tongue")
|
|
77
|
+
(has-class? event "obstruent")
|
|
78
|
+
(has-class? event "nasal")
|
|
79
|
+
(has-class? event "liquid")))
|
|
80
|
+
|
|
81
|
+
(defn vocalic-jaw-event? [event]
|
|
82
|
+
;; Diphthong expansion marks the secondary lip target with "diphthong".
|
|
83
|
+
;; Azure W/UW events often arrive only as the W_OO viseme; treat those as
|
|
84
|
+
;; vocalic only when their jaw target is the rounded vowel/glide amount, not
|
|
85
|
+
;; when fallback text generated a low consonantal W.
|
|
86
|
+
(or (has-class? event "vowel")
|
|
87
|
+
(has-class? event "diphthong")
|
|
88
|
+
(and (= (:visemeId event) (:W_OO visemes/canonical-visemes))
|
|
89
|
+
(>= (raw-target event) 0.28))))
|
|
90
|
+
|
|
91
|
+
(defn event-kind [event]
|
|
92
|
+
(cond
|
|
93
|
+
(pause-event? event) :pause
|
|
94
|
+
(vocalic-jaw-event? event) :vocalic
|
|
95
|
+
:else :consonant))
|
|
96
|
+
|
|
97
|
+
(defn consonant-target [events]
|
|
98
|
+
;; Consonants should not make the jaw flap. Closures and narrow consonants are
|
|
99
|
+
;; mostly lip/tongue business, so the jaw stays low and stable across the
|
|
100
|
+
;; whole consonant cluster.
|
|
101
|
+
(let [events (vec events)
|
|
102
|
+
raw-max (apply max 0 (map raw-target events))]
|
|
103
|
+
(cond
|
|
104
|
+
(every? bilabial? events) 0
|
|
105
|
+
(some bilabial? events) (min raw-max 0.08)
|
|
106
|
+
(every? narrow-fricative? events) (min raw-max 0.08)
|
|
107
|
+
(some narrow-fricative? events) (min raw-max 0.10)
|
|
108
|
+
(every? tongue-consonant? events) (min raw-max 0.14)
|
|
109
|
+
:else (min raw-max 0.16))))
|
|
110
|
+
|
|
111
|
+
(defn group-target [kind events]
|
|
112
|
+
(case kind
|
|
113
|
+
:pause 0
|
|
114
|
+
:vocalic (apply max 0 (map raw-target events))
|
|
115
|
+
:consonant (consonant-target events)))
|
|
116
|
+
|
|
117
|
+
(defn same-jaw-group? [current next-event]
|
|
118
|
+
(let [kind (event-kind current)
|
|
119
|
+
next-kind (event-kind next-event)
|
|
120
|
+
gap (- (start-ms next-event) (end-ms current))]
|
|
121
|
+
(and (= kind next-kind)
|
|
122
|
+
(<= gap jaw-group-gap-ms)
|
|
123
|
+
(>= gap (- jaw-group-gap-ms)))))
|
|
124
|
+
|
|
125
|
+
(defn raw-groups [events]
|
|
126
|
+
(let [ordered (vec (sort-by :offsetMs events))]
|
|
127
|
+
(loop [remaining ordered
|
|
128
|
+
current []
|
|
129
|
+
groups []]
|
|
130
|
+
(cond
|
|
131
|
+
(empty? remaining)
|
|
132
|
+
(if (seq current) (conj groups current) groups)
|
|
133
|
+
|
|
134
|
+
(empty? current)
|
|
135
|
+
(recur (rest remaining) [(first remaining)] groups)
|
|
136
|
+
|
|
137
|
+
(same-jaw-group? (last current) (first remaining))
|
|
138
|
+
(recur (rest remaining) (conj current (first remaining)) groups)
|
|
139
|
+
|
|
140
|
+
:else
|
|
141
|
+
(recur (rest remaining) [(first remaining)] (conj groups current))))))
|
|
142
|
+
|
|
143
|
+
(defn make-group [events]
|
|
144
|
+
(let [kind (event-kind (first events))
|
|
145
|
+
start (apply min (map start-ms events))
|
|
146
|
+
end (apply max (map end-ms events))]
|
|
147
|
+
{:kind kind
|
|
148
|
+
:startMs start
|
|
149
|
+
:endMs end
|
|
150
|
+
:durationMs (max 1 (- end start))
|
|
151
|
+
:target (group-target kind events)
|
|
152
|
+
:events (vec events)}))
|
|
153
|
+
|
|
154
|
+
(defn closure-group? [group]
|
|
155
|
+
(some bilabial? (:events group)))
|
|
156
|
+
|
|
157
|
+
(defn bridgeable-consonant-group? [group previous next-group]
|
|
158
|
+
;; A short tongue-only consonant between two vowels should not close the jaw
|
|
159
|
+
;; and reopen it as a separate visual beat. Bilabials and narrow fricatives are
|
|
160
|
+
;; excluded because they need visible closure/narrowing.
|
|
161
|
+
(and (= :consonant (:kind group))
|
|
162
|
+
(= :vocalic (:kind previous))
|
|
163
|
+
(= :vocalic (:kind next-group))
|
|
164
|
+
(<= (:durationMs group) 90)
|
|
165
|
+
(not (closure-group? group))
|
|
166
|
+
(not-any? narrow-fricative? (:events group))))
|
|
167
|
+
|
|
168
|
+
(defn contextualize-group [groups index group]
|
|
169
|
+
(let [previous (get groups (dec index))
|
|
170
|
+
next-group (get groups (inc index))]
|
|
171
|
+
(if (bridgeable-consonant-group? group previous next-group)
|
|
172
|
+
(let [bridge-target (* jaw-bridge-ratio (min (:target previous) (:target next-group)))]
|
|
173
|
+
(update group :target #(max % bridge-target)))
|
|
174
|
+
group)))
|
|
175
|
+
|
|
176
|
+
(defn jaw-groups [events]
|
|
177
|
+
(let [groups (mapv make-group (raw-groups events))]
|
|
178
|
+
(mapv #(contextualize-group groups %1 %2) (range) groups)))
|
|
179
|
+
|
|
180
|
+
(defn push-frame [curve time-sec intensity]
|
|
181
|
+
(if (or (not (finite-number? time-sec))
|
|
182
|
+
(not (finite-number? intensity)))
|
|
183
|
+
curve
|
|
184
|
+
(let [frame {:time (max 0 time-sec)
|
|
185
|
+
:intensity (state/clamp 0 2 intensity)}
|
|
186
|
+
previous (last curve)]
|
|
187
|
+
(if (and previous (< (js/Math.abs (- (:time previous) (:time frame))) 0.001))
|
|
188
|
+
(assoc-in curve [(dec (count curve)) :intensity] (:intensity frame))
|
|
189
|
+
(conj curve frame)))))
|
|
190
|
+
|
|
191
|
+
(defn deduplicate-curve [curve]
|
|
192
|
+
(let [frames (vec curve)]
|
|
193
|
+
(if (<= (count frames) 1)
|
|
194
|
+
frames
|
|
195
|
+
(loop [remaining (rest frames)
|
|
196
|
+
result [(first frames)]]
|
|
197
|
+
(if (empty? remaining)
|
|
198
|
+
(vec result)
|
|
199
|
+
(let [curr (first remaining)
|
|
200
|
+
prev (last result)
|
|
201
|
+
intensity-changed? (> (js/Math.abs (- (:intensity curr) (:intensity prev))) intensity-eps)
|
|
202
|
+
time-changed? (> (js/Math.abs (- (:time curr) (:time prev))) 0.001)]
|
|
203
|
+
(recur (rest remaining)
|
|
204
|
+
(if (or intensity-changed? time-changed?)
|
|
205
|
+
(conj result curr)
|
|
206
|
+
result))))))))
|
|
207
|
+
|
|
208
|
+
(defn group-attack-sec [group]
|
|
209
|
+
(/ (min (if (= :vocalic (:kind group)) jaw-vowel-attack-ms jaw-consonant-attack-ms)
|
|
210
|
+
(max 6 (* (:durationMs group) 0.35)))
|
|
211
|
+
1000))
|
|
212
|
+
|
|
213
|
+
(defn group-release-sec [group]
|
|
214
|
+
(/ (min (if (= :vocalic (:kind group)) jaw-vowel-release-ms jaw-consonant-release-ms)
|
|
215
|
+
(max 10 (* (:durationMs group)
|
|
216
|
+
(if (= :vocalic (:kind group)) 0.22 0.45))))
|
|
217
|
+
1000))
|
|
218
|
+
|
|
219
|
+
(defn connected-to-next? [group next-group]
|
|
220
|
+
(and next-group
|
|
221
|
+
(<= (- (:startMs next-group) (:endMs group)) jaw-long-gap-ms)))
|
|
222
|
+
|
|
223
|
+
(defn build-jaw-curve [events jaw-scale]
|
|
224
|
+
(let [scale (state/clamp 0 2 (state/number-or jaw-scale 1))
|
|
225
|
+
groups (jaw-groups events)
|
|
226
|
+
has-jaw? (some #(> (* scale (:target %)) intensity-eps) groups)]
|
|
227
|
+
(if-not has-jaw?
|
|
228
|
+
[]
|
|
229
|
+
(loop [index 0
|
|
230
|
+
curve []]
|
|
231
|
+
(if (>= index (count groups))
|
|
232
|
+
(->> curve (sort-by :time) deduplicate-curve vec)
|
|
233
|
+
(let [group (get groups index)
|
|
234
|
+
previous (get groups (dec index))
|
|
235
|
+
next-group (get groups (inc index))
|
|
236
|
+
start-sec (/ (:startMs group) 1000)
|
|
237
|
+
end-sec (/ (:endMs group) 1000)
|
|
238
|
+
target (* scale (:target group))
|
|
239
|
+
attack-sec (group-attack-sec group)
|
|
240
|
+
release-sec (group-release-sec group)
|
|
241
|
+
previous-end-sec (if previous (/ (:endMs previous) 1000) 0)
|
|
242
|
+
starts-after-gap? (or (nil? previous)
|
|
243
|
+
(> (- start-sec previous-end-sec) (/ jaw-long-gap-ms 1000)))
|
|
244
|
+
curve-a (if starts-after-gap? (push-frame curve start-sec 0) curve)
|
|
245
|
+
peak-time (if (> target intensity-eps)
|
|
246
|
+
(+ start-sec attack-sec)
|
|
247
|
+
start-sec)
|
|
248
|
+
curve-b (push-frame curve-a peak-time target)
|
|
249
|
+
connected? (connected-to-next? group next-group)]
|
|
250
|
+
(recur (inc index)
|
|
251
|
+
(if connected?
|
|
252
|
+
(let [next-start-sec (/ (:startMs next-group) 1000)
|
|
253
|
+
transition-time (max peak-time
|
|
254
|
+
(- next-start-sec (/ jaw-transition-lead-ms 1000)))]
|
|
255
|
+
(push-frame curve-b transition-time target))
|
|
256
|
+
(-> curve-b
|
|
257
|
+
(push-frame (max peak-time (- end-sec release-sec)) target)
|
|
258
|
+
(push-frame end-sec 0))))))))))
|