@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,611 @@
|
|
|
1
|
+
(ns polymer.transcription.effector)
|
|
2
|
+
|
|
3
|
+
;; This namespace is the only Transcription code allowed to touch browser
|
|
4
|
+
;; recognition, microphone, AudioContext, or animation-frame APIs. The planner
|
|
5
|
+
;; proposes work and the scheduler invokes this private effector.
|
|
6
|
+
|
|
7
|
+
(defn runtime-value
|
|
8
|
+
[runtime key]
|
|
9
|
+
(cond
|
|
10
|
+
(map? runtime) (get runtime key)
|
|
11
|
+
runtime (aget runtime (name key))
|
|
12
|
+
:else nil))
|
|
13
|
+
|
|
14
|
+
(defn recognition-constructor
|
|
15
|
+
[]
|
|
16
|
+
(let [win (when (exists? js/window) js/window)]
|
|
17
|
+
(or (when win (aget win "SpeechRecognition"))
|
|
18
|
+
(when win (aget win "webkitSpeechRecognition")))))
|
|
19
|
+
|
|
20
|
+
(defn create-recognition
|
|
21
|
+
[runtime]
|
|
22
|
+
(if-let [factory (runtime-value runtime :createRecognition)]
|
|
23
|
+
(factory)
|
|
24
|
+
(when-let [ctor (recognition-constructor)]
|
|
25
|
+
(ctor.))))
|
|
26
|
+
|
|
27
|
+
(defn create-audio-context
|
|
28
|
+
[runtime]
|
|
29
|
+
(if-let [factory (runtime-value runtime :createAudioContext)]
|
|
30
|
+
(factory)
|
|
31
|
+
(let [win (when (exists? js/window) js/window)
|
|
32
|
+
ctor (or (when win (aget win "AudioContext"))
|
|
33
|
+
(when win (aget win "webkitAudioContext")))]
|
|
34
|
+
(when ctor
|
|
35
|
+
(ctor.)))))
|
|
36
|
+
|
|
37
|
+
(defn media-devices
|
|
38
|
+
[]
|
|
39
|
+
(let [navigator (when (exists? js/navigator) js/navigator)]
|
|
40
|
+
(when navigator (.-mediaDevices navigator))))
|
|
41
|
+
|
|
42
|
+
(defn apply-config!
|
|
43
|
+
[recognition config]
|
|
44
|
+
(when recognition
|
|
45
|
+
(set! (.-lang recognition) (:lang config))
|
|
46
|
+
(set! (.-continuous recognition) (boolean (:continuous config)))
|
|
47
|
+
(set! (.-interimResults recognition) (boolean (:interimResults config)))
|
|
48
|
+
(set! (.-maxAlternatives recognition) (:maxAlternatives config))))
|
|
49
|
+
|
|
50
|
+
(defn result-commands
|
|
51
|
+
[event session-id provider-token interim-results?]
|
|
52
|
+
(let [results (.-results event)
|
|
53
|
+
start (or (.-resultIndex event) 0)
|
|
54
|
+
total (if results (.-length results) 0)]
|
|
55
|
+
(loop [index start
|
|
56
|
+
commands []]
|
|
57
|
+
(if (< index total)
|
|
58
|
+
(let [result (aget results index)
|
|
59
|
+
alternative (when result (aget result 0))
|
|
60
|
+
text (when alternative (.-transcript alternative))
|
|
61
|
+
confidence (when alternative (.-confidence alternative))
|
|
62
|
+
final? (boolean (when result (.-isFinal result)))]
|
|
63
|
+
(recur (inc index)
|
|
64
|
+
(cond-> commands
|
|
65
|
+
(and text (or final? interim-results?))
|
|
66
|
+
(conj {:type (if final? "providerFinal" "providerPartial")
|
|
67
|
+
:text text
|
|
68
|
+
:confidence (if (number? confidence) confidence 1)
|
|
69
|
+
:source "webSpeech"
|
|
70
|
+
:sessionId session-id
|
|
71
|
+
:providerToken provider-token}))))
|
|
72
|
+
commands))))
|
|
73
|
+
|
|
74
|
+
(defn clear-handler!
|
|
75
|
+
[recognition handler-name]
|
|
76
|
+
(when recognition
|
|
77
|
+
(aset recognition handler-name nil)))
|
|
78
|
+
|
|
79
|
+
(defn detach-handlers!
|
|
80
|
+
[recognition]
|
|
81
|
+
(doseq [handler-name ["onstart" "onresult" "onerror" "onend"]]
|
|
82
|
+
(clear-handler! recognition handler-name)))
|
|
83
|
+
|
|
84
|
+
(defn disconnect!
|
|
85
|
+
[node]
|
|
86
|
+
(when node
|
|
87
|
+
(try
|
|
88
|
+
(.disconnect node)
|
|
89
|
+
(catch :default _ nil))))
|
|
90
|
+
|
|
91
|
+
(defn stop-media-stream!
|
|
92
|
+
[stream]
|
|
93
|
+
(when stream
|
|
94
|
+
(doseq [track (array-seq (.getTracks stream))]
|
|
95
|
+
(try
|
|
96
|
+
(.stop track)
|
|
97
|
+
(catch :default _ nil)))))
|
|
98
|
+
|
|
99
|
+
(defn read-level
|
|
100
|
+
[analyser data]
|
|
101
|
+
(if (and analyser data)
|
|
102
|
+
(do
|
|
103
|
+
(.getByteTimeDomainData analyser data)
|
|
104
|
+
(let [length (.-length data)]
|
|
105
|
+
(loop [index 0
|
|
106
|
+
sum 0]
|
|
107
|
+
(if (< index length)
|
|
108
|
+
(let [sample (/ (- (aget data index) 128) 128)]
|
|
109
|
+
(recur (inc index) (+ sum (* sample sample))))
|
|
110
|
+
(js/Math.sqrt (/ sum (max 1 length)))))))
|
|
111
|
+
0))
|
|
112
|
+
|
|
113
|
+
(defn live-track
|
|
114
|
+
[stream]
|
|
115
|
+
(when stream
|
|
116
|
+
(some #(when (= "live" (.-readyState %)) %)
|
|
117
|
+
(array-seq (.getAudioTracks stream)))))
|
|
118
|
+
|
|
119
|
+
(defn create-effector
|
|
120
|
+
[{:keys [dispatch get-state runtime]}]
|
|
121
|
+
(let [recognition (atom nil)
|
|
122
|
+
active-token (atom 0)
|
|
123
|
+
failed-tokens (atom #{})
|
|
124
|
+
retry-timer (atom nil)
|
|
125
|
+
disposed? (atom false)
|
|
126
|
+
mic-stream (atom nil)
|
|
127
|
+
mic-request (atom nil)
|
|
128
|
+
analysis-context (atom nil)
|
|
129
|
+
mic-analyser (atom nil)
|
|
130
|
+
mic-analyser-data (atom nil)
|
|
131
|
+
mic-source-node (atom nil)
|
|
132
|
+
reference-track (atom nil)
|
|
133
|
+
reference-analyser (atom nil)
|
|
134
|
+
reference-analyser-data (atom nil)
|
|
135
|
+
reference-source-node (atom nil)
|
|
136
|
+
reference-update-token (atom 0)
|
|
137
|
+
agent-speaking? (atom false)
|
|
138
|
+
interruption-frame (atom nil)
|
|
139
|
+
interruption-candidate-start (atom nil)
|
|
140
|
+
interruption-latched? (atom false)
|
|
141
|
+
last-debug-at (atom 0)]
|
|
142
|
+
(letfn [(current-config []
|
|
143
|
+
(:config (get-state)))
|
|
144
|
+
|
|
145
|
+
(current-token? [token]
|
|
146
|
+
(and (not @disposed?)
|
|
147
|
+
(= token @active-token)))
|
|
148
|
+
|
|
149
|
+
(cancel-retry! []
|
|
150
|
+
(when-let [timer @retry-timer]
|
|
151
|
+
(js/clearTimeout timer)
|
|
152
|
+
(reset! retry-timer nil)))
|
|
153
|
+
|
|
154
|
+
(invalidate-provider! []
|
|
155
|
+
(swap! active-token inc)
|
|
156
|
+
(reset! failed-tokens #{}))
|
|
157
|
+
|
|
158
|
+
(retire-current-provider! [instance token]
|
|
159
|
+
(when (and (= instance @recognition)
|
|
160
|
+
(current-token? token))
|
|
161
|
+
(swap! active-token inc)
|
|
162
|
+
(reset! recognition nil)
|
|
163
|
+
(detach-handlers! instance)
|
|
164
|
+
true))
|
|
165
|
+
|
|
166
|
+
(abort-retired-provider! [instance]
|
|
167
|
+
(try
|
|
168
|
+
(when-let [abort (aget instance "abort")]
|
|
169
|
+
(.call abort instance))
|
|
170
|
+
(catch :default _ nil)))
|
|
171
|
+
|
|
172
|
+
(request-frame! [callback]
|
|
173
|
+
(if-let [request-frame (runtime-value runtime :requestAnimationFrame)]
|
|
174
|
+
(request-frame callback)
|
|
175
|
+
(let [win (when (exists? js/window) js/window)]
|
|
176
|
+
(when (and win (.-requestAnimationFrame win))
|
|
177
|
+
(.requestAnimationFrame win callback)))))
|
|
178
|
+
|
|
179
|
+
(cancel-frame! [frame]
|
|
180
|
+
(if-let [cancel-frame (runtime-value runtime :cancelAnimationFrame)]
|
|
181
|
+
(cancel-frame frame)
|
|
182
|
+
(let [win (when (exists? js/window) js/window)]
|
|
183
|
+
(when (and win (.-cancelAnimationFrame win))
|
|
184
|
+
(.cancelAnimationFrame win frame)))))
|
|
185
|
+
|
|
186
|
+
(clock-ms []
|
|
187
|
+
(let [performance (when (exists? js/performance) js/performance)]
|
|
188
|
+
(if performance (.now performance) (.now js/Date))))
|
|
189
|
+
|
|
190
|
+
(stop-interruption-monitoring! []
|
|
191
|
+
(when-let [frame @interruption-frame]
|
|
192
|
+
(cancel-frame! frame)
|
|
193
|
+
(reset! interruption-frame nil))
|
|
194
|
+
(reset! interruption-candidate-start nil))
|
|
195
|
+
|
|
196
|
+
(schedule-interruption-frame! [tick]
|
|
197
|
+
(when-let [frame (request-frame! tick)]
|
|
198
|
+
(reset! interruption-frame frame)))
|
|
199
|
+
|
|
200
|
+
(start-interruption-monitoring! []
|
|
201
|
+
(stop-interruption-monitoring!)
|
|
202
|
+
(when (and @agent-speaking?
|
|
203
|
+
(:interruptDetectionEnabled (current-config)))
|
|
204
|
+
(letfn [(tick []
|
|
205
|
+
(reset! interruption-frame nil)
|
|
206
|
+
(if (or @disposed?
|
|
207
|
+
(not @agent-speaking?)
|
|
208
|
+
(not (:interruptDetectionEnabled
|
|
209
|
+
(current-config))))
|
|
210
|
+
(stop-interruption-monitoring!)
|
|
211
|
+
(let [config (current-config)
|
|
212
|
+
live-reference? (and @reference-track
|
|
213
|
+
(= "live"
|
|
214
|
+
(.-readyState
|
|
215
|
+
@reference-track))
|
|
216
|
+
@reference-analyser)]
|
|
217
|
+
(if (and (:requireAgentReferenceForInterruption config)
|
|
218
|
+
(not live-reference?))
|
|
219
|
+
(do
|
|
220
|
+
(reset! interruption-candidate-start nil)
|
|
221
|
+
(schedule-interruption-frame! tick))
|
|
222
|
+
(let [microphone-level
|
|
223
|
+
(read-level @mic-analyser @mic-analyser-data)
|
|
224
|
+
reference-level
|
|
225
|
+
(if live-reference?
|
|
226
|
+
(read-level @reference-analyser
|
|
227
|
+
@reference-analyser-data)
|
|
228
|
+
0)
|
|
229
|
+
required-level
|
|
230
|
+
(js/Math.max
|
|
231
|
+
(:interruptionVolumeThreshold config)
|
|
232
|
+
(+ (* reference-level
|
|
233
|
+
(:interruptionReferenceScale config))
|
|
234
|
+
(:interruptionReferenceOffset config)))
|
|
235
|
+
interrupt? (>= microphone-level
|
|
236
|
+
required-level)
|
|
237
|
+
now (clock-ms)]
|
|
238
|
+
(when (and (:interruptionDebugLogging config)
|
|
239
|
+
(>= (- now @last-debug-at) 500))
|
|
240
|
+
(reset! last-debug-at now)
|
|
241
|
+
(js/console.debug
|
|
242
|
+
"[Transcription] Interruption levels"
|
|
243
|
+
#js {:microphoneLevel microphone-level
|
|
244
|
+
:referenceLevel reference-level
|
|
245
|
+
:requiredLevel required-level
|
|
246
|
+
:shouldInterrupt interrupt?
|
|
247
|
+
:hasAgentReference live-reference?}))
|
|
248
|
+
(if (and interrupt?
|
|
249
|
+
(not @interruption-latched?))
|
|
250
|
+
(if-let [candidate-start
|
|
251
|
+
@interruption-candidate-start]
|
|
252
|
+
(when (>= (- now candidate-start)
|
|
253
|
+
(:interruptionHoldMs config))
|
|
254
|
+
(reset! interruption-latched? true)
|
|
255
|
+
(dispatch
|
|
256
|
+
{:type "providerAudioInterruption"
|
|
257
|
+
:timestamp (.now js/Date)
|
|
258
|
+
:microphoneLevel microphone-level
|
|
259
|
+
:referenceLevel reference-level
|
|
260
|
+
:requiredLevel required-level}))
|
|
261
|
+
(reset! interruption-candidate-start now))
|
|
262
|
+
(when-not interrupt?
|
|
263
|
+
(reset! interruption-candidate-start nil)))
|
|
264
|
+
(schedule-interruption-frame! tick))))))]
|
|
265
|
+
(schedule-interruption-frame! tick))))
|
|
266
|
+
|
|
267
|
+
(ensure-analysis-context! []
|
|
268
|
+
(if @disposed?
|
|
269
|
+
(.resolve js/Promise nil)
|
|
270
|
+
(try
|
|
271
|
+
(when (or (nil? @analysis-context)
|
|
272
|
+
(= "closed" (.-state @analysis-context)))
|
|
273
|
+
(reset! analysis-context (create-audio-context runtime)))
|
|
274
|
+
(let [context @analysis-context]
|
|
275
|
+
(if (and context (= "suspended" (.-state context)))
|
|
276
|
+
(-> (.resume context)
|
|
277
|
+
(.then (fn [_] context))
|
|
278
|
+
(.catch (fn [_] context)))
|
|
279
|
+
(.resolve js/Promise context)))
|
|
280
|
+
(catch :default error
|
|
281
|
+
(.reject js/Promise error)))))
|
|
282
|
+
|
|
283
|
+
(ensure-microphone-analyser! []
|
|
284
|
+
(-> (ensure-analysis-context!)
|
|
285
|
+
(.then
|
|
286
|
+
(fn [context]
|
|
287
|
+
(when (and (not @disposed?) context @mic-stream)
|
|
288
|
+
(let [track (live-track @mic-stream)
|
|
289
|
+
old-source @mic-source-node
|
|
290
|
+
old-track (when old-source
|
|
291
|
+
(live-track
|
|
292
|
+
(aget old-source "mediaStream")))]
|
|
293
|
+
(when (and track (not= track old-track))
|
|
294
|
+
(disconnect! old-source)
|
|
295
|
+
(disconnect! @mic-analyser)
|
|
296
|
+
(let [source (.createMediaStreamSource
|
|
297
|
+
context @mic-stream)
|
|
298
|
+
analyser (.createAnalyser context)]
|
|
299
|
+
(set! (.-fftSize analyser) 2048)
|
|
300
|
+
(set! (.-smoothingTimeConstant analyser) 0.65)
|
|
301
|
+
(.connect source analyser)
|
|
302
|
+
(reset! mic-source-node source)
|
|
303
|
+
(reset! mic-analyser analyser)
|
|
304
|
+
(reset! mic-analyser-data
|
|
305
|
+
(js/Uint8Array.
|
|
306
|
+
(.-fftSize analyser)))))))))))
|
|
307
|
+
|
|
308
|
+
(request-microphone! []
|
|
309
|
+
(if-let [get-user-media (runtime-value runtime :getUserMedia)]
|
|
310
|
+
(.resolve js/Promise (get-user-media))
|
|
311
|
+
(if-let [media (media-devices)]
|
|
312
|
+
(let [supported (if (.-getSupportedConstraints media)
|
|
313
|
+
(.getSupportedConstraints media)
|
|
314
|
+
#js {})
|
|
315
|
+
constraints
|
|
316
|
+
(clj->js
|
|
317
|
+
(cond-> {:echoCancellation true
|
|
318
|
+
:noiseSuppression true
|
|
319
|
+
:autoGainControl true}
|
|
320
|
+
(boolean (aget supported "voiceIsolation"))
|
|
321
|
+
(assoc :voiceIsolation true)))]
|
|
322
|
+
(-> (.getUserMedia media #js {:audio constraints})
|
|
323
|
+
(.catch (fn [_]
|
|
324
|
+
(.getUserMedia media #js {:audio true})))))
|
|
325
|
+
;; Worker and Node tests have no mediaDevices. Recognition can
|
|
326
|
+
;; still start through an injected provider.
|
|
327
|
+
(.resolve js/Promise nil))))
|
|
328
|
+
|
|
329
|
+
(ensure-microphone-stream! []
|
|
330
|
+
(cond
|
|
331
|
+
@disposed?
|
|
332
|
+
(.resolve js/Promise nil)
|
|
333
|
+
|
|
334
|
+
(live-track @mic-stream)
|
|
335
|
+
(-> (ensure-microphone-analyser!)
|
|
336
|
+
(.then (fn [_] @mic-stream)))
|
|
337
|
+
|
|
338
|
+
@mic-request
|
|
339
|
+
(:promise @mic-request)
|
|
340
|
+
|
|
341
|
+
:else
|
|
342
|
+
(let [token (js-obj)
|
|
343
|
+
requested
|
|
344
|
+
(-> (request-microphone!)
|
|
345
|
+
(.then
|
|
346
|
+
(fn [stream]
|
|
347
|
+
(if @disposed?
|
|
348
|
+
(do
|
|
349
|
+
(stop-media-stream! stream)
|
|
350
|
+
nil)
|
|
351
|
+
(do
|
|
352
|
+
(reset! mic-stream stream)
|
|
353
|
+
(-> (ensure-microphone-analyser!)
|
|
354
|
+
(.then
|
|
355
|
+
(fn [_]
|
|
356
|
+
(if @disposed?
|
|
357
|
+
(do
|
|
358
|
+
(when (= stream @mic-stream)
|
|
359
|
+
(reset! mic-stream nil))
|
|
360
|
+
(stop-media-stream! stream)
|
|
361
|
+
nil)
|
|
362
|
+
stream)))))))))
|
|
363
|
+
settled
|
|
364
|
+
(-> requested
|
|
365
|
+
(.then
|
|
366
|
+
(fn [stream]
|
|
367
|
+
(when (identical? token (:token @mic-request))
|
|
368
|
+
(reset! mic-request nil))
|
|
369
|
+
stream))
|
|
370
|
+
(.catch
|
|
371
|
+
(fn [error]
|
|
372
|
+
(when (identical? token (:token @mic-request))
|
|
373
|
+
(reset! mic-request nil))
|
|
374
|
+
(throw error))))]
|
|
375
|
+
(reset! mic-request {:token token :promise settled})
|
|
376
|
+
settled)))
|
|
377
|
+
|
|
378
|
+
(replace-reference-analyser! [track]
|
|
379
|
+
(let [token (swap! reference-update-token inc)]
|
|
380
|
+
(reset! reference-track track)
|
|
381
|
+
(disconnect! @reference-source-node)
|
|
382
|
+
(disconnect! @reference-analyser)
|
|
383
|
+
(reset! reference-source-node nil)
|
|
384
|
+
(reset! reference-analyser nil)
|
|
385
|
+
(reset! reference-analyser-data nil)
|
|
386
|
+
(when (and track (= "live" (.-readyState track)))
|
|
387
|
+
(-> (ensure-analysis-context!)
|
|
388
|
+
(.then
|
|
389
|
+
(fn [context]
|
|
390
|
+
(when (and context
|
|
391
|
+
(not @disposed?)
|
|
392
|
+
(= token @reference-update-token)
|
|
393
|
+
(= track @reference-track)
|
|
394
|
+
(= "live" (.-readyState track)))
|
|
395
|
+
(let [stream-constructor
|
|
396
|
+
(or (runtime-value runtime :createMediaStream)
|
|
397
|
+
(when (exists? js/MediaStream)
|
|
398
|
+
(fn [tracks]
|
|
399
|
+
(js/MediaStream. tracks))))
|
|
400
|
+
stream (when stream-constructor
|
|
401
|
+
(stream-constructor #js [track]))]
|
|
402
|
+
(when stream
|
|
403
|
+
(let [source (.createMediaStreamSource
|
|
404
|
+
context stream)
|
|
405
|
+
analyser (.createAnalyser context)]
|
|
406
|
+
(set! (.-fftSize analyser) 2048)
|
|
407
|
+
(set! (.-smoothingTimeConstant analyser) 0.65)
|
|
408
|
+
(.connect source analyser)
|
|
409
|
+
(if (and (= token @reference-update-token)
|
|
410
|
+
(= track @reference-track)
|
|
411
|
+
(= "live" (.-readyState track)))
|
|
412
|
+
(do
|
|
413
|
+
(reset! reference-source-node source)
|
|
414
|
+
(reset! reference-analyser analyser)
|
|
415
|
+
(reset! reference-analyser-data
|
|
416
|
+
(js/Uint8Array.
|
|
417
|
+
(.-fftSize analyser))))
|
|
418
|
+
(do
|
|
419
|
+
(disconnect! source)
|
|
420
|
+
(disconnect! analyser)))))))))
|
|
421
|
+
(.catch (fn [_] nil))))
|
|
422
|
+
(when @agent-speaking?
|
|
423
|
+
(start-interruption-monitoring!))))
|
|
424
|
+
|
|
425
|
+
(sync-agent-speaking! [speaking?]
|
|
426
|
+
(reset! agent-speaking? (boolean speaking?))
|
|
427
|
+
(reset! interruption-latched? false)
|
|
428
|
+
(reset! interruption-candidate-start nil)
|
|
429
|
+
(if @agent-speaking?
|
|
430
|
+
(-> (ensure-microphone-analyser!)
|
|
431
|
+
(.then (fn [_]
|
|
432
|
+
(start-interruption-monitoring!)))
|
|
433
|
+
(.catch (fn [_] nil)))
|
|
434
|
+
(stop-interruption-monitoring!)))
|
|
435
|
+
|
|
436
|
+
(stop-provider! [method-name]
|
|
437
|
+
(cancel-retry!)
|
|
438
|
+
(stop-interruption-monitoring!)
|
|
439
|
+
(let [instance @recognition]
|
|
440
|
+
(invalidate-provider!)
|
|
441
|
+
(reset! recognition nil)
|
|
442
|
+
(when instance
|
|
443
|
+
(detach-handlers! instance)
|
|
444
|
+
(try
|
|
445
|
+
(when-let [method (aget instance method-name)]
|
|
446
|
+
(.call method instance))
|
|
447
|
+
(catch :default _ nil)))
|
|
448
|
+
(.resolve js/Promise nil)))
|
|
449
|
+
|
|
450
|
+
(fail-provider! [instance token session-id message]
|
|
451
|
+
(when (and (current-token? token)
|
|
452
|
+
(not (contains? @failed-tokens token)))
|
|
453
|
+
(swap! failed-tokens conj token)
|
|
454
|
+
(when (retire-current-provider! instance token)
|
|
455
|
+
(abort-retired-provider! instance)
|
|
456
|
+
(dispatch {:type "providerError"
|
|
457
|
+
:message message
|
|
458
|
+
:sessionId session-id
|
|
459
|
+
:providerToken token}))))
|
|
460
|
+
|
|
461
|
+
(start-recognition-instance! [instance track]
|
|
462
|
+
(if track
|
|
463
|
+
(try
|
|
464
|
+
(.start instance track)
|
|
465
|
+
(catch :default _
|
|
466
|
+
(.start instance)))
|
|
467
|
+
(.start instance)))
|
|
468
|
+
|
|
469
|
+
(start-provider! [session-id config]
|
|
470
|
+
(cancel-retry!)
|
|
471
|
+
(if @disposed?
|
|
472
|
+
(.resolve js/Promise nil)
|
|
473
|
+
(if-let [instance (create-recognition runtime)]
|
|
474
|
+
(let [token (swap! active-token inc)
|
|
475
|
+
begin! (fn [stream]
|
|
476
|
+
(when (current-token? token)
|
|
477
|
+
(try
|
|
478
|
+
(start-recognition-instance!
|
|
479
|
+
instance (live-track stream))
|
|
480
|
+
(catch :default error
|
|
481
|
+
(fail-provider!
|
|
482
|
+
instance token session-id
|
|
483
|
+
(or (.-message error)
|
|
484
|
+
"speech-recognition-start-error"))))))]
|
|
485
|
+
(reset! recognition instance)
|
|
486
|
+
(apply-config! instance config)
|
|
487
|
+
(set! (.-onstart instance)
|
|
488
|
+
(fn []
|
|
489
|
+
(when (current-token? token)
|
|
490
|
+
(dispatch {:type "providerStarted"
|
|
491
|
+
:sessionId session-id
|
|
492
|
+
:providerToken token})
|
|
493
|
+
(when @agent-speaking?
|
|
494
|
+
(start-interruption-monitoring!)))))
|
|
495
|
+
(set! (.-onresult instance)
|
|
496
|
+
(fn [event]
|
|
497
|
+
(when (current-token? token)
|
|
498
|
+
(doseq [command
|
|
499
|
+
(result-commands
|
|
500
|
+
event
|
|
501
|
+
session-id
|
|
502
|
+
token
|
|
503
|
+
(get-in (get-state)
|
|
504
|
+
[:config :interimResults]))]
|
|
505
|
+
(dispatch command)))))
|
|
506
|
+
(set! (.-onerror instance)
|
|
507
|
+
(fn [event]
|
|
508
|
+
(fail-provider!
|
|
509
|
+
instance
|
|
510
|
+
token
|
|
511
|
+
session-id
|
|
512
|
+
(or (.-error event)
|
|
513
|
+
"speech-recognition-error"))))
|
|
514
|
+
(set! (.-onend instance)
|
|
515
|
+
(fn []
|
|
516
|
+
(when (retire-current-provider! instance token)
|
|
517
|
+
(stop-interruption-monitoring!)
|
|
518
|
+
(dispatch {:type "providerEnded"
|
|
519
|
+
:sessionId session-id
|
|
520
|
+
:providerToken token}))))
|
|
521
|
+
(if (or (runtime-value runtime :getUserMedia)
|
|
522
|
+
(media-devices))
|
|
523
|
+
(-> (ensure-microphone-stream!)
|
|
524
|
+
(.then begin!)
|
|
525
|
+
(.catch
|
|
526
|
+
(fn [error]
|
|
527
|
+
(fail-provider!
|
|
528
|
+
instance token session-id
|
|
529
|
+
(or (.-message error)
|
|
530
|
+
"microphone-start-error"))))
|
|
531
|
+
(.then (fn [_] nil)))
|
|
532
|
+
(do
|
|
533
|
+
(begin! nil)
|
|
534
|
+
(.resolve js/Promise nil))))
|
|
535
|
+
(do
|
|
536
|
+
(dispatch {:type "providerError"
|
|
537
|
+
:message "Web Speech recognition is not available"
|
|
538
|
+
:sessionId session-id})
|
|
539
|
+
(.resolve js/Promise nil)))))
|
|
540
|
+
|
|
541
|
+
(retry-provider! [session-id delay-ms]
|
|
542
|
+
(cancel-retry!)
|
|
543
|
+
(when @recognition
|
|
544
|
+
(stop-provider! "abort"))
|
|
545
|
+
(reset! retry-timer
|
|
546
|
+
(js/setTimeout
|
|
547
|
+
(fn []
|
|
548
|
+
(reset! retry-timer nil)
|
|
549
|
+
(let [current-state (get-state)]
|
|
550
|
+
(when (and (not @disposed?)
|
|
551
|
+
(:active current-state)
|
|
552
|
+
(= session-id (:sessionId current-state)))
|
|
553
|
+
(-> (start-provider!
|
|
554
|
+
session-id (:config current-state))
|
|
555
|
+
(.catch (fn [_] nil))))))
|
|
556
|
+
delay-ms))
|
|
557
|
+
nil)
|
|
558
|
+
|
|
559
|
+
(dispose-browser-resources! []
|
|
560
|
+
(stop-interruption-monitoring!)
|
|
561
|
+
(swap! reference-update-token inc)
|
|
562
|
+
(reset! reference-track nil)
|
|
563
|
+
(stop-media-stream! @mic-stream)
|
|
564
|
+
(reset! mic-stream nil)
|
|
565
|
+
(disconnect! @mic-source-node)
|
|
566
|
+
(disconnect! @mic-analyser)
|
|
567
|
+
(disconnect! @reference-source-node)
|
|
568
|
+
(disconnect! @reference-analyser)
|
|
569
|
+
(reset! mic-source-node nil)
|
|
570
|
+
(reset! mic-analyser nil)
|
|
571
|
+
(reset! mic-analyser-data nil)
|
|
572
|
+
(reset! reference-source-node nil)
|
|
573
|
+
(reset! reference-analyser nil)
|
|
574
|
+
(reset! reference-analyser-data nil)
|
|
575
|
+
(when-let [context @analysis-context]
|
|
576
|
+
(try (.close context) (catch :default _ nil)))
|
|
577
|
+
(reset! analysis-context nil))]
|
|
578
|
+
{:start
|
|
579
|
+
(fn [session-id config]
|
|
580
|
+
(start-provider! session-id config))
|
|
581
|
+
|
|
582
|
+
:stop
|
|
583
|
+
(fn []
|
|
584
|
+
(stop-provider! "stop"))
|
|
585
|
+
|
|
586
|
+
:abort
|
|
587
|
+
(fn []
|
|
588
|
+
(stop-provider! "abort"))
|
|
589
|
+
|
|
590
|
+
:retry
|
|
591
|
+
(fn [session-id delay-ms]
|
|
592
|
+
(retry-provider! session-id delay-ms))
|
|
593
|
+
|
|
594
|
+
:configure
|
|
595
|
+
(fn [config]
|
|
596
|
+
(apply-config! @recognition config)
|
|
597
|
+
(if (and @agent-speaking?
|
|
598
|
+
(:interruptDetectionEnabled config))
|
|
599
|
+
(start-interruption-monitoring!)
|
|
600
|
+
(stop-interruption-monitoring!))
|
|
601
|
+
nil)
|
|
602
|
+
|
|
603
|
+
:set-reference-track replace-reference-analyser!
|
|
604
|
+
:sync-agent-speaking sync-agent-speaking!
|
|
605
|
+
|
|
606
|
+
:dispose
|
|
607
|
+
(fn []
|
|
608
|
+
(when-not @disposed?
|
|
609
|
+
(reset! disposed? true)
|
|
610
|
+
(stop-provider! "abort")
|
|
611
|
+
(dispose-browser-resources!)))})))
|