Search code examples
androidtext-to-speechgoogle-text-to-speech

What is the correct way to display a Voice object to the user in an Android app using tts engine com.google.android.tts?


An Android app running API > 21 utilizes the framework TextToSpeech class. The tts engine is set as com.google.android.tts.

All the available locales in the engine can be obtained with,

Set<Locale> engineLocales = mTts.getAvailableLanguages();

and,

String displayLanguage = locale.getDisplayLanguage();

creates a nice user-facing string "English, Chinese, Spanish, etc." Great.

All the available voices in the engine can be obtained with,

Set<Voice> engineVoices = mTts.getVoices();

and this set can be inspected to determine what voices are actually installed on the device. Perfect, a set of voices supported by the engine and on the device has been obtained.

My question is: how to properly display the Voice name? There is no convenient getDisplayName() or similar on a Voice object. The name property of the Voice object is a string that can not be shown to the user "ko-kr-x-kod-local, pt-pt-x-jmn-network, fr-fr-x-frd-local, etc."

Do I need to hardcode a name:displayName map? This is bad practice as Google can change the name anytime. It could be handled as "Voice I, Voice II, Voice III, etc." but I find it hard to believe a consistent display name is not available somewhere. Have I missed it? Any thoughts?


Solution

  • There doesn't seem to be such a function. Pretty silly.

    Personally, I would just let the device/engine choose the "best" voice and only let the user specify the language. The Google engine is going to automatically select the most high quality and various other factors.

    But, if you really want to allow the user to choose a specific voice from a list, you could at least do something like "French 1," "French 2..." instead of "Voice 1," "Voice 2..." and it wouldn't require hard coding names:

    (unchecked code just to illustrate the idea:)

    Voice[] frenchVoices = new ArrayList<Voice>();
    
    for (Voice v : engineVoices) {
    
        if v.name.contains("fr") {
            frenchVoices.add(v);
        }
    
    }
    

    Then when you show these voices to the user, just iterate and label them. If the available voices change, though, then "French 1" could suddenly become a different voice.