Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
482 views
in Technique[技术] by (71.8m points)

android - How to poll available TTS engines for available languages without instantiating each one and wating for init

I need to instantiate a TextToSpeech object and set a given language (which is set programmatically and may vary). I know I can use setLanguage() but that will only work if the language is available in the TTS engine that partictual TextToSpeech instance is using. I know I can check whether a language is available by means of myTTS.isLanguageAvailable() but that will only tell me whether the language is available on the current engine.

The problem is that the user may have more than one TTS engine installed and the desired language may be available in one of them but not in the default one. In that case I want to find the engine, use it and set the language.

So I need to loop through the available TTS engines and "ask" each one whether it has the desired language available.

I have tried this:

mUserLocale=new Locale("it-IT"); //just an example

mTextToSpeech=new TextToSpeech(getApplicationContext(), this);
if (mTextToSpeech.isLanguageAvailable(mUserLocale)<0) {

    List<TextToSpeech.EngineInfo> engines=mTextToSpeech.getEngines();
    int currentmatchquality=-1;
    String defaultTTSEngine=mTextToSpeech.getDefaultEngine();
    mTextToSpeech.shutdown();
    mTextToSpeech=null;
    for (int i=0; i<engines.size(); i++) {
        TextToSpeech.EngineInfo engineinfo=engines.get(i);
        Log.d("MainActivity", "Examining TTS engine "+engineinfo.name);
        if (engineinfo.name.equals(defaultTTSEngine)) {
            Log.d("MainActivity", "Skipping default TTS engine "+engineinfo.name);
            continue;
        }
        TextToSpeech candidateTTS=new TextToSpeech(getApplicationContext(),this,engineinfo.name);
        int matchquality=candidateTTS.isLanguageAvailable(mUserLocale);
        if (matchquality>currentmatchquality) {
            Log.d("MainActivity", "Selecting TTS engine "+engineinfo.name);
            mTextToSpeech.shutdown();
            mTextToSpeech=candidateTTS;
            mTTSEngine=engineinfo.name;
            currentmatchquality=matchquality;

        }
        else {
            Log.d("MainActivity", "   "+mUserLocale.toString()+" not available on this engine: "+matchquality);
        }
    }
    if (mTTSEngine==null) mTTSEngine=defaultTTSEngine;
    mTextToSpeech=new TextToSpeech(getApplicationContext(),this,mTTSEngine);
}

if (mTextToSpeech.isLanguageAvailable(mUserLocale)>=0) mTextToSpeech.setLanguage(mUserLocale);

The problem is that I systematically get -2 as the result from isLanguageAvailable:

D/MainActivity﹕ Examining TTS engine com.google.android.tts
I/TextToSpeech﹕ Sucessfully bound to com.google.android.tts
W/TextToSpeech﹕ isLanguageAvailable failed: not bound to TTS engine
D/MainActivity﹕ it-it not available on this engine: -2

I guess it's because I need to wait for the init event of the TTS before I query it for an available language.

That would be cumbersome. Is there a way to loop through existing TTS engines and check whether a language is available in each of them (or get a list of all available languages per engine) without instantiating a TextToSpeech object for each engine and waiting for it to be initialized??

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here is the setup I use for debugging - You could use elements of it in production:

// Container Class

public class ContainerVoiceEngine {

    private String label;
    private String packageName;
    private ArrayList<String> voices;
    private Intent intent;

    public ContainerVoiceEngine() {

    }

    public ContainerVoiceEngine(final String label, final String packageName, final ArrayList<String> voices, final Intent intent) {

        this.label = label;
        this.packageName = packageName;
        this.voices = voices;
        this.intent = intent;
    }

    public Intent getIntent() {
        return intent;
    }

    public void setIntent(final Intent intent) {
        this.intent = intent;
    }

    public String getLabel() {
        return label;
    }

    public void setLabel(final String label) {
        this.label = label;
    }

    public String getPackageName() {
        return packageName;
    }

    public void setPackageName(final String packageName) {
        this.packageName = packageName;
    }

    public ArrayList<String> getVoices() {
        return voices;
    }

    public void setVoices(final ArrayList<String> voices) {
        this.voices = voices;
    }
}


// Usage within an Activity - Debugging only!

  private ArrayList<ContainerVoiceEngine> containerVEArray;
    private int requestCount;

    private void getEngines() {

        requestCount = 0;

        final Intent ttsIntent = new Intent();
        ttsIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);

        final PackageManager pm = getPackageManager();

        final List<ResolveInfo> list = pm.queryIntentActivities(ttsIntent, PackageManager.GET_META_DATA);

        containerVEArray = new ArrayList<ContainerVoiceEngine>(list.size());

        for (int i = 0; i < list.size(); i++) {

            final ContainerVoiceEngine cve = new ContainerVoiceEngine();

            cve.setLabel(list.get(i).loadLabel(pm).toString());
            cve.setPackageName(list.get(i).activityInfo.applicationInfo.packageName);

            final Intent getIntent = new Intent();
            getIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);

            getIntent.setPackage(cve.getPackageName());
            getIntent.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
            getIntent.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);

            cve.setIntent(getIntent);

            containerVEArray.add(cve);
        }

        Log.d("TAG", "containerVEArray: " + containerVEArray.size());

        for (int i = 0; i < containerVEArray.size(); i++) {
            startActivityForResult(containerVEArray.get(i).getIntent(), i);
        }

    }

    @Override
    public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        Log.i("TAG", "onActivityResult: requestCount: " + " - requestCode: " + requestCode);

        requestCount++;

        try {

            if (data != null) {

                final Bundle bundle = data.getExtras();

                if (bundle != null) {

                    Log.d("TAG", containerVEArray.get(requestCode).getLabel() + " - Bundle Data");

                    final Set<String> keys = bundle.keySet();
                    final Iterator<String> it = keys.iterator();

                    while (it.hasNext()) {
                        final String key = it.next();
                        Log.d("TAG", "Key: " + key + " = " + bundle.get(key));
                    }

                }

                if (data.hasExtra("availableVoices")) {
                    containerVEArray.get(requestCode).setVoices(data.getStringArrayListExtra("availableVoices"));
                } else {
                    containerVEArray.get(requestCode).setVoices(new ArrayList<String>());
                }
            }

            if (requestCount == containerVEArray.size()) {

                for (int i = 0; i < containerVEArray.size(); i++) {

                    Log.v("TAG", "cve: " + containerVEArray.get(i).getLabel() + " - "
                            + containerVEArray.get(i).getVoices().size() + " - " + containerVEArray.get(i).getVoices().toString());
                }
            }

        } catch (final IndexOutOfBoundsException e) {
            Log.e("TAG", "IndexOutOfBoundsException");
            e.printStackTrace();
        } catch (final NullPointerException e) {
            Log.e("TAG", "NullPointerException");
            e.printStackTrace();
        } catch (final Exception e) {
            Log.e("TAG", "Exception");
            e.printStackTrace();
        }
    }

Hope that helps.

Edit

Further to your comment, these 'voices' are in actual fact the languages, in the sense that they return a representative Locale in their structure. Be aware though, the Locale is in a different format to the System Locale, which is really annoying and needs to be sorted in a future Android update.

Take a look at my answer here regarding the pitfalls with using tts.setLanguage(Locale). The question also deals with creating a Locale - Locale loc = new Locale(String).

You can use a loop for the ArrayList of available languages to try and construct a Locale from each, for each Engine.

Hope that helps.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...