如何针对可用语言轮询可用的TTS引擎,而无需实例化每个引擎并等待init

时间:2014-09-06 12:18:07

标签: android text-to-speech

我需要实例化一个TextToSpeech对象并设置一个给定的语言(以编程方式设置并可能不同)。 我知道我可以使用setLanguage(),但只有在TTS引擎中有特定TextToSpeech实例使用的语言时才能使用。 我知道我可以通过myTTS.isLanguageAvailable()检查语言是否可用,但这只能告诉我当前引擎上是否有该语言。

问题是用户可能安装了多个TTS引擎,并且其中一个可能会使用所需的语言,但不能使用默认语言。在这种情况下,我想找到引擎,使用它并设置语言。

所以我需要遍历可用的TTS引擎并“询问”每个引擎是否具有所需的语言。

我试过这个:

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);

问题是我从isLanguageAvailable系统地得到-2:

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

我想这是因为在查询可用语言之前,我需要等待TTS的init事件。

那会很麻烦。 有没有办法循环遍历现有的TTS引擎并检查每个引擎中是否有可用的语言(或获取每个引擎的所有可用语言的列表),而无需为每个引擎实例化TextToSpeech对象并等待它被初始化?

1 个答案:

答案 0 :(得分:4)

以下是我用于调试的设置 - 您可以在生产中使用它的元素:

// 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();
        }
    }

希望有所帮助。

修改

除了你的评论之外,这些'声音'实际上是语言,因为它们在结构中返回一个代表性的Locale。请注意,Locale与System Locale的格式不同,这非常烦人,需要在未来的Android更新中进行排序。

使用tts.setLanguage(Locale)查看有关陷阱的my answer here。该问题还涉及创建Locale - Locale loc = new Locale(String)。

您可以使用可用语言的ArrayList循环来尝试为每个引擎构建一个Locale。

希望有所帮助。