Adom TTS: Text to Speech
Public Made by Adomby adom
Adom's shared voice service. Reads your AI's answers back to you while you drive, and renders perfectly-timed voiceover tracks for your demo videos, so you never record your own voice.
Shared player wedges silently with two desktops connected: unpinned drainer, ambiguous browser_open_window, no fail-and-advance
Summary
On a machine with two Adom Desktops connected to the same relay (a laptop + a second box), the shared player wedges: clips synthesize and enqueue fine, but nothing is ever heard. status reports now playing "x.mp3" on :43073 at 0s / 0s indefinitely, the queue only grows, and check <clip> stays PLAYING (or QUEUED) forever. A ~10 second clip never reaches PLAYED. Observed live on adom-tts 1.9.29 while trying to narrate hands-free to a driving user, who heard nothing across roughly a dozen clips.
Root cause (fairly confident)
The drainer cannot open its player surface, and the failure is ambiguous-target, which the im-driving skill explicitly warns about:
If the relay has >1 client, the pup player's
browser_open_windowis AMBIGUOUS and fails silently. Pin it to the user's host:export ADOM_DESKTOP_TARGET=<host>.
The event log is unambiguous about the mechanism (~59 of these in one session):
open_surface_failed error="adom-desktop browser_open_window failed: " surface="pup"
The problem is whose environment the drainer runs in. The drainer is a long-lived background process. It was started earlier, without ADOM_DESKTOP_TARGET set. Exporting ADOM_DESKTOP_TARGET in the shell that later runs say does not reach the already-running drainer, so its browser_open_window calls stay unpinned, stay ambiguous with two desktops connected, and fail every time. No surface opens, so there is no audio sink, so clips sit "playing" at 0s/0s.
browser_list_windows from a pinned shell returned 5 sessions the whole time, so the bridge itself was up and answering. It is specifically the drainer's own unpinned surface-open that fails.
Three things that turn a transient failure into a stuck player
A wedged clip at the head of the queue blocks everything behind it. Once one clip is stuck "playing" with no sink, every later clip is
QUEUEDbehind it forever. A clip that cannot be sunk within N seconds should fail and advance, not block the queue.statusreports it as actively playing.now playing "x" on :43073at0s / 0sreads as healthy while the audio position never advances. A0s / 0sthat never moves should surface as stalled, not as playing. Right now the only way to know it is dead is to notice the queue growing.reopendid not recover it, and killing the:43073owner did not either,statuscame back reporting the same pid. Whatever keeps the drainer alive (auto-heal onstatus, a supervisor) resurrects it into the same broken, unpinned state rather than a fresh pinned one, so there was no clean way to reset it short of a host restart.
Also minor: adom-desktop status reports browser.bridgeRunning: false while browser_list_windows returns 5 live sessions, so that field is misleading during triage.
Repro
- Connect two Adom Desktops to the same relay (e.g. a laptop and a second machine).
- From a shell that has NOT exported
ADOM_DESKTOP_TARGET, let the drainer start (anysay --play). adom-tts say "test" --play --surface pup, later, even from a shell that DOES exportADOM_DESKTOP_TARGET.adom-tts statusshowsnow playing ...stuck at0s / 0s, queue grows,checknever reachesPLAYED.adom-tts logsshows repeatedopen_surface_failed ... browser_open_window failed ... surface="pup".
Suggested fixes
- Pin the drainer to a target at spawn, from
ADOM_DESKTOP_TARGET(or an explicit--target), and re-resolve it when the environment changes, so itsbrowser_open_windowis never ambiguous when multiple desktops are connected. This is the core fix. - Fail-and-advance instead of block: a clip that cannot open a surface (or cannot start audio) within a short deadline should be marked failed and the queue should move on, so one bad clip cannot silence the whole session.
- Honest
status:0s / 0sthat does not advance for more than a few seconds should read asstalled/no sink, notplaying. - A real reset path: a
stop/restartthat fully tears down and respawns the drainer pinned and clean (todayreopenre-offers the tab but keeps the broken player; killing:43073respawned the same broken pid). - Fix the misleading
browser.bridgeRunning: falsewhen the bridge is actually answering.
Impact
Hands-free is exactly the situation where the user cannot look at the screen, so a silent failure that still reports "playing" is the worst possible mode: the person is told nothing and hears nothing. The feature and the HD-side lightbulb prompt are fine; the shared player's surface-open and stall handling are what need hardening.
Environment: adom-tts 1.9.29, two Adom Desktops on one relay (laptop AdomLapper + ConfRoomROG), user in pup window mode, HD 0.1.223 / AD 1.9.165.