@lovelace_lol/polymer 0.0.7 → 0.0.9

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 (95) hide show
  1. package/README.md +434 -12
  2. package/dist/index.d.ts +1945 -244
  3. package/dist/index.js +1686 -1017
  4. package/dist/vision/face-worker.js +296 -0
  5. package/dist/vision/face_landmarker.task +0 -0
  6. package/dist/vision/manifest.edn +1 -0
  7. package/dist/vision/vision_bundle.js +2 -0
  8. package/dist/vision/wasm/vision_wasm_internal.js +8841 -0
  9. package/dist/vision/wasm/vision_wasm_internal.wasm +0 -0
  10. package/dist/vision/wasm/vision_wasm_module_internal.js +8837 -0
  11. package/dist/vision/wasm/vision_wasm_module_internal.wasm +0 -0
  12. package/dist/vision/wasm/vision_wasm_nosimd_internal.js +8832 -0
  13. package/dist/vision/wasm/vision_wasm_nosimd_internal.wasm +0 -0
  14. package/dist/worker/agency-worker.js +1276 -962
  15. package/dist/worker/manifest.edn +1 -1
  16. package/package.json +33 -20
  17. package/dist/test.cjs +0 -1519
  18. package/shadow-cljs.edn +0 -173
  19. package/src/polymer/animation/agency.cljs +0 -84
  20. package/src/polymer/animation/domain.cljs +0 -110
  21. package/src/polymer/animation/planner.cljs +0 -88
  22. package/src/polymer/animation/runtime.cljs +0 -137
  23. package/src/polymer/animation/scheduler.cljs +0 -178
  24. package/src/polymer/animation/service.cljs +0 -981
  25. package/src/polymer/animation/state.cljs +0 -112
  26. package/src/polymer/blink/agency.cljs +0 -131
  27. package/src/polymer/blink/planner.cljs +0 -115
  28. package/src/polymer/blink/scheduler.cljs +0 -90
  29. package/src/polymer/blink/snippet.cljs +0 -50
  30. package/src/polymer/blink/state.cljs +0 -102
  31. package/src/polymer/camera_context/agency.cljs +0 -132
  32. package/src/polymer/camera_context/domain.cljs +0 -186
  33. package/src/polymer/camera_context/planner.cljs +0 -50
  34. package/src/polymer/camera_context/scheduler.cljs +0 -113
  35. package/src/polymer/camera_context/state.cljs +0 -121
  36. package/src/polymer/character.cljs +0 -403
  37. package/src/polymer/conversation/agency.cljs +0 -83
  38. package/src/polymer/conversation/domain.cljs +0 -35
  39. package/src/polymer/conversation/effector.cljs +0 -130
  40. package/src/polymer/conversation/planner.cljs +0 -178
  41. package/src/polymer/conversation/scheduler.cljs +0 -202
  42. package/src/polymer/conversation/service.cljs +0 -329
  43. package/src/polymer/conversation/state.cljs +0 -186
  44. package/src/polymer/core.cljs +0 -172
  45. package/src/polymer/eye_head/agency.cljs +0 -75
  46. package/src/polymer/eye_head/domain.cljs +0 -133
  47. package/src/polymer/eye_head/goap.cljs +0 -106
  48. package/src/polymer/eye_head/scheduler.cljs +0 -176
  49. package/src/polymer/eye_head/service.cljs +0 -259
  50. package/src/polymer/eye_head/snippet.cljs +0 -92
  51. package/src/polymer/eye_head/state.cljs +0 -167
  52. package/src/polymer/gaze/agency.cljs +0 -85
  53. package/src/polymer/gaze/domain.cljs +0 -211
  54. package/src/polymer/gaze/goap.cljs +0 -208
  55. package/src/polymer/gaze/scheduler.cljs +0 -184
  56. package/src/polymer/gaze/state.cljs +0 -202
  57. package/src/polymer/gesture/agency.cljs +0 -185
  58. package/src/polymer/gesture/goap.cljs +0 -461
  59. package/src/polymer/gesture/scheduler.cljs +0 -122
  60. package/src/polymer/gesture/snippet.cljs +0 -198
  61. package/src/polymer/gesture/state.cljs +0 -273
  62. package/src/polymer/lipsync/agency.cljs +0 -593
  63. package/src/polymer/lipsync/articulation/jaw.cljs +0 -258
  64. package/src/polymer/lipsync/articulation/snippet.cljs +0 -605
  65. package/src/polymer/lipsync/articulation/tongue.cljs +0 -418
  66. package/src/polymer/lipsync/articulation/visemes.cljs +0 -429
  67. package/src/polymer/lipsync/goap.cljs +0 -132
  68. package/src/polymer/lipsync/pronunciation/effector.cljs +0 -51
  69. package/src/polymer/lipsync/scheduler.cljs +0 -302
  70. package/src/polymer/lipsync/state.cljs +0 -202
  71. package/src/polymer/nlp/phonemes.cljs +0 -164
  72. package/src/polymer/prosodic/agency.cljs +0 -196
  73. package/src/polymer/prosodic/goap.cljs +0 -122
  74. package/src/polymer/prosodic/scheduler.cljs +0 -99
  75. package/src/polymer/prosodic/snippet.cljs +0 -106
  76. package/src/polymer/prosodic/state.cljs +0 -139
  77. package/src/polymer/runtime/embody.cljs +0 -117
  78. package/src/polymer/runtime/embody_wasm.cljs +0 -177
  79. package/src/polymer/runtime/rust_embody_host.cljs +0 -647
  80. package/src/polymer/society.cljs +0 -253
  81. package/src/polymer/stream.cljs +0 -130
  82. package/src/polymer/transcription/agency.cljs +0 -69
  83. package/src/polymer/transcription/domain.cljs +0 -68
  84. package/src/polymer/transcription/effector.cljs +0 -611
  85. package/src/polymer/transcription/planner.cljs +0 -203
  86. package/src/polymer/transcription/scheduler.cljs +0 -225
  87. package/src/polymer/transcription/service.cljs +0 -209
  88. package/src/polymer/transcription/state.cljs +0 -239
  89. package/src/polymer/tts/agency.cljs +0 -503
  90. package/src/polymer/tts/azure.cljs +0 -290
  91. package/src/polymer/tts/planner.cljs +0 -178
  92. package/src/polymer/tts/runtime.cljs +0 -624
  93. package/src/polymer/tts/scheduler.cljs +0 -162
  94. package/src/polymer/tts/state.cljs +0 -191
  95. package/src/polymer/worker.cljs +0 -81
@@ -1,503 +0,0 @@
1
- (ns polymer.tts.agency
2
- (:require [polymer.stream :as stream]
3
- [polymer.tts.azure :as azure]
4
- [polymer.tts.planner :as planner]
5
- [polymer.tts.runtime :refer [azure-cache-key
6
- azure-voice-name
7
- backend-url
8
- callback-event
9
- cleanup-audio!
10
- clear-display-media-reference!
11
- custom-provider
12
- dispose-playback-resources!
13
- ensure-web-speech-display-media-reference!
14
- get-playback-reference-track
15
- js-error
16
- load-azure-voices!
17
- load-web-speech-voices!
18
- normalize-voices
19
- now-ms
20
- plan-failure-message
21
- play-azure-audio!
22
- prime-audio!
23
- remember-cache
24
- scalar->azure-percent
25
- scalar->azure-pitch-percent
26
- speak-web-speech!
27
- speech-synthesis*
28
- synthesize-azure!
29
- stop-web-speech-handle!]]
30
- [polymer.tts.scheduler :as scheduler]
31
- [polymer.tts.state :as state]))
32
-
33
- ;; TTS owns speech-provider side effects and emits plain data facts for LipSync.
34
- ;; The impure browser/backend/audio calls live in `polymer.tts.runtime`; this
35
- ;; namespace owns command planning, session state, and callback routing.
36
-
37
- (defn create-tts-agency
38
- "Create a TTS agency that owns provider sessions and emits LipSync facts."
39
- [config]
40
- (let [initial-config (js->clj config :keywordize-keys true)
41
- input-stream (stream/create-stream)
42
- event-stream (stream/create-stream)
43
- emit-input (:emit input-stream)
44
- emit-event (:emit event-stream)
45
- state-atom (atom (state/default-state initial-config))
46
- resources (atom {:audio nil
47
- :audioUrl nil
48
- :audioSource nil
49
- :webSpeechHandle nil
50
- :audioContext nil
51
- :playbackReferenceDestination nil
52
- :mediaElementSource nil
53
- :playbackGain nil
54
- :displayMediaReferenceStream nil
55
- :displayMediaReferenceTrack nil})
56
- session-scheduler (scheduler/create-scheduler)
57
- azure-cache (atom {})
58
- voice-load-seq (atom 0)
59
- voice-load-requests (atom {})
60
- playback-reference-listeners (atom #{})
61
- disposed? (atom false)]
62
- (letfn [(session-id []
63
- ;; Every provider callback captures the session id active when
64
- ;; it was registered. Replacements/stops increment the token so
65
- ;; late callbacks cannot mutate state or emit stale LipSync data.
66
- (:sessionId @state-atom))
67
-
68
- (active-session? [id]
69
- (and (not @disposed?) (= id (session-id))))
70
-
71
- (notify-playback-reference-track! []
72
- ;; Host-facing handle for barge-in. Stream messages stay plain data;
73
- ;; MediaStreamTrack is exposed only through this imperative API.
74
- (let [track (get-playback-reference-track resources)]
75
- (doseq [listener @playback-reference-listeners]
76
- (listener track))
77
- (emit-event {:type "ttsPlaybackReferenceChanged"
78
- :agency "tts"
79
- :available (boolean track)
80
- :at (now-ms)})
81
- track))
82
-
83
- (emit-status! []
84
- ;; Status events are coarse facts, not animation ticks. The
85
- ;; agency emits word/viseme progress through streams instead of
86
- ;; making a host poll mutable state.
87
- (emit-event {:type "ttsStatusChanged"
88
- :agency "tts"
89
- :state (state/visible-state @state-atom)}))
90
-
91
- (emit-error! [message]
92
- (swap! state-atom state/record-error message)
93
- (emit-event {:type "error" :agency "tts" :message message})
94
- (emit-status!))
95
-
96
- (emit-lipsync! [command]
97
- ;; This is an event-stream bridge inside Polymer. The agency
98
- ;; system routes this command to LipSync, which then emits
99
- ;; animation intent for the Animation agency.
100
- (emit-event {:type "lipSync.command"
101
- :agency "tts"
102
- :command command}))
103
-
104
- (active-speech? []
105
- (contains? #{"loading" "speaking"} (:status @state-atom)))
106
-
107
- (next-voice-load! [engine]
108
- (let [request-id (swap! voice-load-seq inc)]
109
- (swap! voice-load-requests assoc engine request-id)
110
- request-id))
111
-
112
- (active-voice-load? [engine request-id]
113
- (and (not @disposed?)
114
- (= request-id (get @voice-load-requests engine))))
115
-
116
- (cancel-voice-loads! []
117
- (swap! voice-load-requests empty))
118
-
119
- (stop-web-speech! []
120
- (when-let [handle (:webSpeechHandle @resources)]
121
- (stop-web-speech-handle! handle))
122
- (swap! resources assoc :webSpeechHandle nil))
123
-
124
- (configure-lipsync! [engine command]
125
- ;; TTS knows provider timing and user speech controls; LipSync knows
126
- ;; how those controls become mouth/jaw/tongue animation.
127
- (let [config (:config @state-atom)]
128
- (emit-lipsync! {:type "configure"
129
- :config {:intensity (:lipsyncIntensity config)
130
- :jawScale (:jawScale config)
131
- :tongueScale (:tongueScale config)
132
- :speechRate (or (:rate command) (:rate config))
133
- :visualLeadMs (or (:visualLeadMs command) (:visualLeadMs config))
134
- :wordDriftThresholdSec (if (= engine "azure")
135
- (:azureDriftThresholdSec config)
136
- (:webSpeechDriftThresholdSec config))}})))
137
-
138
- (stop-session! [reason]
139
- ((:stop-active-session session-scheduler) reason)
140
- (stop-web-speech!)
141
- (cleanup-audio! resources)
142
- (emit-lipsync! {:type "stop" :reason reason})
143
- (swap! state-atom state/record-stopped (now-ms))
144
- (emit-event {:type "ttsSpeechStopped"
145
- :agency "tts"
146
- :reason reason
147
- :stoppedAt (:endedAt @state-atom)})
148
- (emit-status!))
149
-
150
- (finish-session! [id]
151
- (when (active-session? id)
152
- (stop-session! "completed")
153
- (emit-event {:type "ttsSpeechEnded"
154
- :agency "tts"
155
- :endedAt (:endedAt @state-atom)})))
156
-
157
- (word-boundary! [id word observed-elapsed-sec host-elapsed-sec]
158
- (when (and (active-session? id) (pos? (count (or word ""))))
159
- (let [word-index (:wordIndex @state-atom)]
160
- (swap! state-atom state/record-word-boundary word-index)
161
- (emit-lipsync! {:type "wordBoundary"
162
- :word word
163
- :wordIndex word-index
164
- :observedElapsedSec observed-elapsed-sec
165
- :hostElapsedSec host-elapsed-sec})
166
- (emit-event {:type "ttsWordBoundary"
167
- :agency "tts"
168
- :word word
169
- :wordIndex word-index
170
- :observedElapsedSec observed-elapsed-sec
171
- :hostElapsedSec host-elapsed-sec}))))
172
-
173
- (start-web-speech! [id command snippet-name]
174
- (let [config (:config @state-atom)
175
- text (:text command)
176
- plan {:text text
177
- :voiceName (or (:voiceName command) (:voiceName config))
178
- :rate (or (:rate command) (:rate config))
179
- :pitch (or (:pitch command) (:pitch config))
180
- :volume (or (:volume command) (:volume config))
181
- :onAudioStarted (fn [_event]
182
- (when (active-session? id)
183
- (configure-lipsync! "webSpeech" command)
184
- (emit-lipsync! {:type "startText"
185
- :name snippet-name
186
- :text text
187
- :source "webSpeech"})
188
- (emit-lipsync! {:type "audioStarted"
189
- :name snippet-name
190
- :audioTimeSec 0})
191
- (swap! state-atom state/record-speaking (now-ms))
192
- (emit-event {:type "ttsSpeechStarted"
193
- :agency "tts"
194
- :engine "webSpeech"
195
- :name snippet-name
196
- :startedAt (:startedAt @state-atom)})
197
- (emit-status!)))
198
- :onBoundary (fn [event]
199
- (let [payload (callback-event event)]
200
- (word-boundary! id
201
- (:word payload)
202
- (:observedElapsedSec payload)
203
- (:hostElapsedSec payload))))
204
- :onEnd (fn [] (finish-session! id))
205
- :onError (fn [error] (emit-error! (.-message error)))
206
- :scheduleStartFallback
207
- (fn [delay-ms callback]
208
- ((:schedule-start-fallback session-scheduler) id delay-ms callback))
209
- :cancelStartFallback
210
- (fn []
211
- ((:cancel-start-fallback session-scheduler) id))}]
212
- (-> (speak-web-speech! config plan)
213
- (.then (fn [handle]
214
- (if (active-session? id)
215
- (swap! resources assoc :webSpeechHandle handle)
216
- (stop-web-speech-handle! handle))))
217
- (.catch (fn [error]
218
- (when (active-session? id)
219
- (emit-error! (.-message error))))))))
220
-
221
- (start-azure! [id command snippet-name]
222
- (let [config (:config @state-atom)
223
- text (:text command)
224
- voice-name (azure-voice-name config command)
225
- style (let [raw (or (:style command) (:azureStyle config))]
226
- (when (and (string? raw) (pos? (count (.trim raw)))) raw))
227
- rate (or (:rate command) (:rate config))
228
- pitch (or (:pitch command) (:pitch config))
229
- volume (or (:volume command) (:volume config))
230
- cache-key (azure-cache-key text voice-name style
231
- (scalar->azure-percent rate)
232
- (scalar->azure-pitch-percent pitch))
233
- cached (get @azure-cache cache-key)
234
- synth-promise (if cached
235
- (js/Promise.resolve (clj->js cached))
236
- (synthesize-azure! config (assoc command
237
- :voiceName voice-name
238
- :style style
239
- :rate rate
240
- :pitch pitch)))]
241
- ;; Prime synchronously while the click/user gesture is still
242
- ;; available. Synthesis may finish later, but the element is
243
- ;; already allowed to play in stricter browsers.
244
- ;; Prime HTMLAudio for autoplay unlock only. Do not advertise the
245
- ;; playback-reference track until real Azure audio is routed, or
246
- ;; barge-in would arm against a silent muted unlock clip.
247
- (prime-audio! resources volume)
248
- (-> synth-promise
249
- (.then (fn [raw]
250
- ;; Provider/backend shapes are normalized before
251
- ;; any playback or LipSync command is emitted.
252
- (let [payload (azure/normalize-azure-synthesis
253
- (if (map? raw)
254
- raw
255
- (js->clj raw :keywordize-keys true)))]
256
- (when-not cached
257
- (swap! azure-cache remember-cache cache-key payload (:azureCacheLimit config)))
258
- ;; Audio is required. Visemes help LipSync but must not
259
- ;; block playback when Azure returns audio alone.
260
- (if (pos? (count (str (:audioBase64 payload))))
261
- payload
262
- (throw (js-error "Azure TTS returned no usable audio payload"))))))
263
- (.then (fn [payload]
264
- (when (active-session? id)
265
- (play-azure-audio! config resources
266
- (assoc payload
267
- :volume volume)))))
268
- (.then (fn [playback]
269
- (when (active-session? id)
270
- (let [payload (if cached cached (get @azure-cache cache-key))
271
- duration-sec (or (:durationSec payload) (aget playback "durationSec") 0)
272
- word-timings (:wordTimings payload)]
273
- (configure-lipsync! "azure" command)
274
- (emit-lipsync! (azure/azure-synthesis->lipsync-command
275
- snippet-name
276
- text
277
- (assoc payload :durationSec duration-sec)
278
- config))
279
- (emit-lipsync! {:type "audioStarted"
280
- :name snippet-name
281
- :audioTimeSec 0})
282
- (swap! state-atom state/record-speaking (now-ms))
283
- (emit-event {:type "ttsSpeechStarted"
284
- :agency "tts"
285
- :engine "azure"
286
- :name snippet-name
287
- :startedAt (:startedAt @state-atom)})
288
- (emit-status!)
289
- (notify-playback-reference-track!)
290
- (when-let [clock (or (:clock playback)
291
- (when (aget playback "clock")
292
- (js->clj (aget playback "clock") :keywordize-keys true)))]
293
- ((:schedule-boundaries session-scheduler)
294
- id
295
- active-session?
296
- clock
297
- word-timings
298
- (fn [event]
299
- (word-boundary! id
300
- (:word event)
301
- (:observedElapsedSec event)
302
- nil))))
303
- (when-let [audio (or (:audio playback) (aget playback "audio"))]
304
- (set! (.-onended audio) #(finish-session! id)))))))
305
- (.catch (fn [error]
306
- (when (active-session? id)
307
- (emit-error! (.-message error))))))))
308
-
309
- (speak! [command]
310
- (let [config (:config @state-atom)
311
- engine (or (:engine command) (:engine config))
312
- text (:text command)
313
- snippet-name (or (:name command) (str "tts_" engine "_" (.now js/Date)))
314
- ;; Provider planning sees only capability facts. It does not
315
- ;; receive JS handles or secrets, and it never performs side
316
- ;; effects.
317
- world {:backendUrl (backend-url config command)
318
- :hasAzureSynthesize (boolean (custom-provider config :azureSynthesize))
319
- :hasWebSpeech (boolean (or (custom-provider config :webSpeechSpeak)
320
- (speech-synthesis*)))}
321
- plan (planner/plan-speech (assoc command :engine engine) config world)]
322
- (swap! state-atom (fn [state]
323
- (-> state
324
- state/next-session
325
- (state/record-plan plan)
326
- (state/record-loading engine text snippet-name))))
327
- (emit-event {:type "ttsPlanCreated"
328
- :agency "tts"
329
- :plan plan})
330
- (emit-status!)
331
- (if-not (:ok plan)
332
- (emit-error! (plan-failure-message plan))
333
- (do
334
- ((:start-session session-scheduler) (session-id) engine snippet-name)
335
- (case engine
336
- "azure" (start-azure! (session-id) command snippet-name)
337
- "webSpeech" (start-web-speech! (session-id) command snippet-name)
338
- (emit-error! (str "Unsupported TTS engine: " engine)))))))
339
-
340
- (load-voices! [engine]
341
- (let [config (:config @state-atom)
342
- ;; Voice loading has its own plan so missing backend/browser
343
- ;; support is visible before any provider request is made.
344
- request-id (next-voice-load! engine)
345
- world {:backendUrl (:backendUrl config)
346
- :hasAzureVoices (boolean (custom-provider config :azureVoices))
347
- :hasWebSpeech (boolean (or (custom-provider config :webSpeechVoices)
348
- (speech-synthesis*)))}
349
- plan (planner/plan-voice-load engine world)]
350
- (emit-event {:type "ttsPlanCreated"
351
- :agency "tts"
352
- :plan plan})
353
- (if-not (:ok plan)
354
- (do
355
- (when (= "azure" engine)
356
- (swap! state-atom
357
- state/record-azure-status
358
- "error"
359
- (plan-failure-message plan)
360
- []))
361
- (emit-error! (plan-failure-message plan)))
362
- (case engine
363
- "webSpeech"
364
- (-> (load-web-speech-voices! config)
365
- (.then (fn [voices]
366
- (when (active-voice-load? engine request-id)
367
- (let [normalized (normalize-voices
368
- (js->clj voices :keywordize-keys true)
369
- "webSpeech")]
370
- (swap! state-atom state/record-web-speech-voices normalized)
371
- (emit-event {:type "ttsVoicesLoaded"
372
- :agency "tts"
373
- :engine "webSpeech"
374
- :voices normalized})
375
- (emit-status!)))))
376
- (.catch (fn [error]
377
- (when (active-voice-load? engine request-id)
378
- (emit-error! (.-message error))))))
379
-
380
- "azure"
381
- (-> (load-azure-voices! config)
382
- (.then (fn [response]
383
- (when (active-voice-load? engine request-id)
384
- (let [data (js->clj response :keywordize-keys true)
385
- voices (normalize-voices (:voices data) "azure")
386
- ready? (and (:valid data) (seq voices))
387
- status (if ready? "ready" "error")
388
- message (if ready?
389
- (str "Connected via backend environment (" (count voices) " voices).")
390
- (or (:message data) "Backend Azure credentials are not configured."))]
391
- (swap! state-atom state/record-azure-status status message voices)
392
- (emit-event {:type "ttsVoicesLoaded"
393
- :agency "tts"
394
- :engine "azure"
395
- :voices voices
396
- :status status
397
- :message message})
398
- (emit-status!)))))
399
- (.catch (fn [error]
400
- (when (active-voice-load? engine request-id)
401
- (swap! state-atom state/record-azure-status "error" (.-message error) [])
402
- (emit-error! (.-message error))))))
403
-
404
- (emit-error! (str "Unsupported voice provider: " engine))))))
405
-
406
- (run-action! [action]
407
- (case (:op action)
408
- "cancel-voice-loads"
409
- (cancel-voice-loads!)
410
-
411
- "configure"
412
- (do
413
- (swap! state-atom state/configure (:config action))
414
- (emit-status!))
415
-
416
- "load-voices"
417
- (load-voices! (:engine action))
418
-
419
- "stop-active-session"
420
- (when (active-speech?)
421
- (stop-session! (:reason action)))
422
-
423
- "advance-session"
424
- (swap! state-atom state/next-session)
425
-
426
- "stop-session"
427
- (stop-session! (:reason action))
428
-
429
- "speak"
430
- (speak! (:command action))
431
-
432
- "reset"
433
- (do
434
- (stop-web-speech!)
435
- ((:stop-all session-scheduler))
436
- (cleanup-audio! resources)
437
- (reset! state-atom (state/default-state nil))
438
- (emit-status!))
439
-
440
- "error"
441
- (emit-error! (:message action))
442
-
443
- (emit-error! (str "Unknown TTS planner action: " (:op action)))))
444
-
445
- (dispatch! [command]
446
- (when-not @disposed?
447
- (let [payload (js->clj command :keywordize-keys true)]
448
- (emit-input {:type "command"
449
- :agency "tts"
450
- :command payload})
451
- (doseq [action (planner/plan-command @state-atom payload)]
452
- (run-action! action)))))]
453
- #js {:dispatch dispatch!
454
- :snapshot (fn [] (state/visible-state @state-atom))
455
- :schedulerQueue (fn [] (clj->js ((:queue session-scheduler))))
456
- :input (stream/writable-port input-stream dispatch!)
457
- :events (stream/readable-port event-stream)
458
- :subscribeInput (fn [listener] ((:subscribe input-stream) listener))
459
- :subscribeEvents (fn [listener] ((:subscribe event-stream) listener))
460
- :subscribe (fn [listener] ((:subscribe event-stream) listener))
461
- :subscribeStatus (fn [listener] ((:subscribe event-stream) listener))
462
- :configure (fn [next-config]
463
- (dispatch! #js {:type "configure" :config next-config})
464
- (when (= "none" (get-in @state-atom [:config :webSpeechReferenceMode]))
465
- (clear-display-media-reference! resources)
466
- (notify-playback-reference-track!)))
467
- :loadVoices (fn
468
- ([] (dispatch! #js {:type "loadVoices"}))
469
- ([engine] (dispatch! #js {:type "loadVoices" :engine engine})))
470
- :speak (fn [text] (dispatch! #js {:type "speak" :text text}))
471
- :stop (fn [] (dispatch! #js {:type "stop"}))
472
- :reset (fn [] (dispatch! #js {:type "reset"}))
473
- :getPlaybackReferenceTrack (fn [] (get-playback-reference-track resources))
474
- :onPlaybackReferenceTrackChange (fn [listener]
475
- (swap! playback-reference-listeners conj listener)
476
- (listener (get-playback-reference-track resources))
477
- (fn []
478
- (swap! playback-reference-listeners disj listener)))
479
- :preparePlaybackReference (fn []
480
- (let [config (:config @state-atom)]
481
- (cond
482
- (and (= "webSpeech" (:engine config))
483
- (= "displayMedia" (:webSpeechReferenceMode config)))
484
- (-> (ensure-web-speech-display-media-reference! resources)
485
- (.then (fn [status]
486
- (notify-playback-reference-track!)
487
- status)))
488
-
489
- (get-playback-reference-track resources)
490
- (js/Promise.resolve "available")
491
-
492
- :else
493
- (js/Promise.resolve "unavailable"))))
494
- :dispose (fn []
495
- (when-not @disposed?
496
- (reset! disposed? true)
497
- (cancel-voice-loads!)
498
- ((:dispose session-scheduler))
499
- (stop-web-speech!)
500
- (dispose-playback-resources! resources)
501
- (reset! playback-reference-listeners #{})
502
- ((:dispose input-stream))
503
- ((:dispose event-stream))))})))