我需要实例化一个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对象并等待它被初始化?
答案 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。
希望有所帮助。