Skip to content

Use the speech queue only on the main thread - #107

Open
aaron-gh wants to merge 2 commits into
trypsynth:masterfrom
aaron-gh:speech-completion-main-thread
Open

aaron-gh wants to merge 2 commits into
trypsynth:masterfrom
aaron-gh:speech-completion-main-thread

Conversation

@aaron-gh

@aaron-gh aaron-gh commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

The speech queue in SpeechControllerImpl, including the speech parameters map it reuses for every fragment, was used from more than one thread at a time. This was already the case in TalkBack:

  • The end of each utterance. UtteranceProgressCallback.onDone, onStop and onError called handleUtteranceCompleted directly. That runs on the engine's callback thread, or the low-latency audio callback thread, and goes on to onFragmentCompleted and processNextFragmentInternal(), which starts the next fragment. The start of an utterance and its word ranges were already handed to the main thread through SpeechHandler. Now the end is too.
  • Screen off. RingerModeAndScreenMonitor works out "Screen off" and the ringer state on a background thread, but it also called service.clearQueues() and spoke from there. The announcement is still worked out in the background. The speech and event queues are now cleared, and it is spoken, on the main thread. If the screen has come back on by then, neither happens.

Meanwhile the main thread could be starting new speech with the same queue and map. Speech could then reach the engine with another fragment's parameters or with no utterance id, which is the crash in #104.

When Backtalk turns off, the main thread waits for the last announcement, so speech callbacks are still handled on the engine's thread then, as before.

When the speech engine, or low-latency audio, reported that an
utterance had finished, the speech controller handled it on the thread
that reported it: the engine's callback thread or the low-latency audio
callback thread. Handling it starts the next fragment, so the speech
queue and its reused speech parameters were used from that thread while
the main thread could be starting new speech with them. Speech could
then go to the engine with another fragment's parameters, or with no
utterance id, which crashed (trypsynth#104).

The end of an utterance is now handed to the main thread, as its start
and its word ranges already were. When Backtalk turns off, the main
thread waits for the last announcement, so callbacks are still handled
on the engine's thread then, as before.
When the screen turned off, Backtalk worked out what to say on a
background thread, as TalkBack does, but it also cleared the speech and
event queues and spoke "Screen off" or the ringer state from there.
That used the speech queue and its reused speech parameters while the
main thread could be using them too.

The announcement is still worked out on the background thread, and the
queues are now cleared and it is spoken on the main thread. If the
screen has come back on by then, neither happens.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant