Skip to content

Android: fall back to available voice when requested language is filtered out#2387

Open
alex19EP wants to merge 1 commit intomasterfrom
android-voice-fallback
Open

Android: fall back to available voice when requested language is filtered out#2387
alex19EP wants to merge 1 commit intomasterfrom
android-voice-fallback

Conversation

@alex19EP
Copy link
Copy Markdown
Member

@alex19EP alex19EP commented Mar 26, 2026

Summary

  • When a screen reader (e.g. Jieshuo) requests the system language for synthesis, but that language is not in the user's language selector, synthesis previously failed silently
  • Now falls back to an available voice from the user's selected set instead of returning an error
  • This fixes the issue where Jieshuo wouldn't work on English-system devices with only Russian selected in the eSpeak language selector (TalkBack was unaffected because it queries available voices first)

Problem

On an English-system Android device with eSpeak's language selector set to Russian only:

  1. Jieshuo sends onLoadLanguage("eng", ...) based on the system language
  2. English is not in the filtered voice set → LANG_NOT_SUPPORTED
  3. selectVoice() returns ERRORmMatchingVoice stays null → synthesis silently fails

TalkBack works because it calls onGetVoices() first and picks an available voice.

Fix

In selectVoice(), when the requested language returns LANG_NOT_SUPPORTED:

  • If mMatchingVoice is already set from a previous load, keep it (reuse the last working voice)
  • If mMatchingVoice is null (fresh start), pick the first available voice from the filtered set
  • LANG_MISSING_DATA still fails hard (no data installed at all)

This effectively "pins" the engine to the user's selected language(s) regardless of what the screen reader requests.

Test plan

  • On English-system Android with only Russian in language selector: verify Jieshuo synthesizes in Russian
  • On Russian-system Android with only Russian selected: verify no regression
  • With multiple languages selected: verify correct language is still chosen when available
  • With all languages selected (default): verify no behavior change
  • APK builds successfully (./gradlew assembleDebug)
  • All native tests pass (ctest --test-dir build)

Copilot AI review requested due to automatic review settings March 26, 2026 23:30
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the Android TextToSpeechService implementation to avoid silent synthesis failures when assistive technologies request a language that’s not present in the user’s filtered voice set, by falling back to an available voice instead of hard-failing.

Changes:

  • Treat LANG_MISSING_DATA as a hard failure in selectVoice().
  • On LANG_NOT_SUPPORTED, fall back to an available voice (reuse mMatchingVoice if already set; otherwise pick one from the filtered set).
Comments suppressed due to low confidence (1)

android/src/com/reecedunn/espeak/TtsService.java:326

  • selectVoice(SynthesisRequest) is currently not called anywhere in TtsService (no references in this class), so this new fallback logic will never run and won’t address the reported “fresh start” failure case where mMatchingVoice is null. To make the fix effective, either invoke selectVoice(request) from the synthesis path (e.g., at the start of onSynthesizeText) or move this fallback into onLoadLanguage, which is part of the framework’s voice-selection API surface.
    private int selectVoice(SynthesisRequest request) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            final String name = request.getVoiceName();
            if (name != null && !name.isEmpty()) {
                return onLoadVoice(name);
            }
        }

        final int result = onLoadLanguage(request.getLanguage(), request.getCountry(), request.getVariant());
        switch (result) {
            case TextToSpeech.LANG_MISSING_DATA:
                return TextToSpeech.ERROR;
            case TextToSpeech.LANG_NOT_SUPPORTED:
                // When the requested language is not in the user's selected set,
                // fall back to an available voice instead of failing. This allows
                // screen readers that request the system language (e.g. Jieshuo)
                // to still work when the user has selected only other languages.
                synchronized (mAvailableVoices) {
                    if (!mAvailableVoices.isEmpty()) {
                        if (mMatchingVoice == null) {
                            mMatchingVoice = mAvailableVoices.values().iterator().next();
                        }
                        return TextToSpeech.SUCCESS;
                    }
                }
                return TextToSpeech.ERROR;
        }
        return TextToSpeech.SUCCESS;
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@alex19EP alex19EP force-pushed the android-voice-fallback branch 3 times, most recently from ac723fa to 912ab51 Compare March 27, 2026 00:09
@alex19EP alex19EP requested a review from Copilot March 27, 2026 00:10
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@alex19EP alex19EP force-pushed the android-voice-fallback branch from 912ab51 to 3957bac Compare March 27, 2026 12:43
…ered out

Screen readers like Jieshuo request the system language for synthesis. When
the system language (e.g. English) is not in the user's language selector
but another language (e.g. Russian) is, synthesis would fail silently.

Fall back to an available voice instead of returning an error, so the engine
always synthesizes in a user-selected language regardless of what the screen
reader requests.

Co-Authored-By: Claude <noreply@anthropic.com>
@alex19EP alex19EP force-pushed the android-voice-fallback branch from 3957bac to 2ed8e8a Compare March 27, 2026 20:04
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.

2 participants