Summary
On Android, stopTts() is a no-op while TTS generation is running. It only takes effect after generation has already finished on its own.
Cause
TtsHandler runs init, generate, stop, and release on a single shared executor:
packages/sherpa-onnx.rn/android/src/main/kotlin/net/siteed/sherpaonnx/handlers/TtsHandler.kt:20
private val executor = Executors.newSingleThreadExecutor()
Call sites at :41 (init), :396 (generate), :745 (stop), :780 (release).
stop() therefore queues behind the in-flight generate() and cannot clear isGenerating while the generation callback loop is reading it. The isGenerating checks inside the callback (:463) and at the short-utterance fallback (:553) can never fire during the generation they are meant to interrupt.
Reproduction
Confirmed on a Pixel 6a via __AGENTIC__.testTTSStopDuringPrefill(120) in apps/sherpa-voice: calling stopTts() 120 ms into generation produced no Stopping TTS generation log line until after generation completed. Logcat showed generation and prefill running to completion uninterrupted.
Current state
isGenerating was marked @Volatile in #434 so the cross-thread write is at least visible. That is a correct partial step but changes no observable behavior — stopTts() during generation is still a no-op. Filing this so the volatile annotation is not later mistaken for evidence the problem was solved.
The limitation is documented in three places (TtsHandler.kt:22-23, TtsHandler.kt:544-551, and the testTTSStopDuringPrefill hook in apps/sherpa-voice/src/agentic-bridge.ts), and testTTSStopDuringPrefill is retained as a regression probe for when this is fixed.
Suggested fix
Run stop() on a separate control executor (or directly, with appropriate synchronization) so it can flip isGenerating while generation is in flight. Note that stop() currently only pauses a track already in PLAYSTATE_PLAYING; a track still being prefilled is STOPPED, so the pause/flush would be skipped. #434 already guards the post-loop playback start on isGenerating in anticipation of exactly that.
Also worth deciding whether iOS has the same behavior, or whether the platforms should be aligned.
Found during cross-review of #434.
Summary
On Android,
stopTts()is a no-op while TTS generation is running. It only takes effect after generation has already finished on its own.Cause
TtsHandlerrunsinit,generate,stop, andreleaseon a single shared executor:packages/sherpa-onnx.rn/android/src/main/kotlin/net/siteed/sherpaonnx/handlers/TtsHandler.kt:20Call sites at
:41(init),:396(generate),:745(stop),:780(release).stop()therefore queues behind the in-flightgenerate()and cannot clearisGeneratingwhile the generation callback loop is reading it. TheisGeneratingchecks inside the callback (:463) and at the short-utterance fallback (:553) can never fire during the generation they are meant to interrupt.Reproduction
Confirmed on a Pixel 6a via
__AGENTIC__.testTTSStopDuringPrefill(120)inapps/sherpa-voice: callingstopTts()120 ms into generation produced noStopping TTS generationlog line until after generation completed. Logcat showed generation and prefill running to completion uninterrupted.Current state
isGeneratingwas marked@Volatilein #434 so the cross-thread write is at least visible. That is a correct partial step but changes no observable behavior —stopTts()during generation is still a no-op. Filing this so the volatile annotation is not later mistaken for evidence the problem was solved.The limitation is documented in three places (
TtsHandler.kt:22-23,TtsHandler.kt:544-551, and thetestTTSStopDuringPrefillhook inapps/sherpa-voice/src/agentic-bridge.ts), andtestTTSStopDuringPrefillis retained as a regression probe for when this is fixed.Suggested fix
Run
stop()on a separate control executor (or directly, with appropriate synchronization) so it can flipisGeneratingwhile generation is in flight. Note thatstop()currently only pauses a track already inPLAYSTATE_PLAYING; a track still being prefilled isSTOPPED, so the pause/flush would be skipped. #434 already guards the post-loop playback start onisGeneratingin anticipation of exactly that.Also worth deciding whether
iOShas the same behavior, or whether the platforms should be aligned.Found during cross-review of #434.