一、介绍
TextToSpeech:将文本合成语音,立即播放或创建声音文件。
最简单的流程如下:
1.创建TextToSpeech后,它会找到一个适合的Engine进行连接,然后回调onInit,如果status不为0,则没有找到引擎。
2.在初始化成功后,调用speak就可以进行语音播报了。播报过程中,引擎会调用UtteranceProgressListener的回调函数,它是个抽象类,可以覆盖其他函数,如onRangeStart(String utteranceId, int start, int end, int frame) 可以在播报过程中可以拿到实时返回的读取字符位置,但它是在api 26以后才开始支持。
简单的TTS语音播报代码如下:
if (textToSpeech == null) {
textToSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
textToSpeech.speak("你好,测试语音播报功能", TextToSpeech.QUEUE_ADD, null);
textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
@Override
public void onStart(String utteranceId) {
}
@Override
public void onDone(String utteranceId) {
}
@Override
public void onError(String utteranceId) {
}
});
}
});
}
在实际场景中,可能会遇到以下问题需要抉择(尤其是在做海外市场)
1.支持引擎(引擎查询和设置)
2.支持语言(语言支持和下载)
3.版本兼容问题(回调版本支持)
接下来分别进行介绍。
二、实践-引擎
1.引擎是TTS核心部分,主要用于加载语言语音包,将文本转换为音频,及执行回调的部分。引擎的实现,需要继承TextToSpeechService,并且需要对以下方法进行实现:
protected abstract int onIsLanguageAvailable(String lang, String country, String variant)
protected abstract String[] onGetLanguage()
protected abstract int onLoadLanguage(String lang, String country, String variant)
protected abstract void onStop()
protected abstract void onSynthesizeText(SynthesisRequest request, SynthesisCallback callback)
详细的原理这里不进行讲解,可以参考下面两篇文章,讲的很详细:
https://blog.csdn.net/qq_30359699/article/details/105388575
https://blog.csdn.net/zhupumao/article/details/78960456
首先,虽然列出了要实现TextToSpeechService的方法,但我们如果只使用系统提供的引擎,则不需要自己继承TextToSpeechService并实现以上方法,但需要知道的是:
1.是否支持语言,支持语言列表及加载语言包:分别是onIsLanguageAvailable,onGetLanguage和onLoadLanguage
2.进行文本合成语音,需要在onSynthesizeText中实现,对应的回调也需要在方法中调用SynthesisCallback的方法
3.onStop停止语音合成
4.因为引擎是自定义实现的,所以会有自己的语言语音包来支持不同的国家语言(如果做海外市场要注意,引擎是否支持对应国家的语言),这些语言语音包依赖于引擎提供方是否提供
5.需要在AndroidManifest中,在自己的Service中,加入如下配置:
<intent-filter>
<action android:name="android.intent.action.TTS_SERVICE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
说了这么多,如果使用系统的引擎,则可以忽略这部分内容,但对理解有帮助。
2.引擎支持,通常在不同品牌设备上,语音引擎可能有多个,并且都是系统级别的,如三星有GoogleTTSService和SamsungTTSService等,这个时候不选择引擎时,很有可能使用的是SamsungTTSService。这里我写了个获取引擎列表的方法如下:
public List<String> getEngines() {
if (mContext == null) {
throw new IllegalArgumentException("Please set mContext first!!");
}
ArrayList<String> engines = new ArrayList<>();
PackageManager pm = mContext.getPackageManager();
Intent intent = new Intent(TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE);
List<ResolveInfo> resolveInfo = pm.queryIntentServices(intent,PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo info: resolveInfo) {
engines.add(info.serviceInfo.packageName);
}
return engines;
}
可以看到,主要是通过action:TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE来进行查询(自定义引擎的Service也必须设置action为TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE)。
而最终得到的,是所有继承TextToSpeechService实现引擎的包名列表,当使用指定引擎时,TextToSpeech会通过包名和action绑定到Service上。
好,不出意外的话,会包含google系统引擎的包名,“com.google.android.tts”,后面说到引擎时,都是继承了TextToSpeechService并实现了语音合成的包名。
3.有了引擎,我们在使用TextToSpeech的时候,就可以使用TextToSpeech(Context context, OnInitListener listener, String engine) 来连接到指定引擎了。
三、实践-Locale
Locale包含了语言,国家和多样性等信息,但并不全包含这些信息,如果使TextToSpeech播放不同国家的语言时,这时需要设置Locale给它。但TextToSpeech一定会播放吗,不一定,需要看引擎是否支持,必定真正工作的是引擎对应的Service。那么看下图:
文本输入给引擎之前,引擎需要加载语言语音包,如果支持指定的语言,则可以输出音频数据进行播放或写入文件,同时会执行回调。那么问题来了,如何知道引擎是否支持语言音乐包,如果不支持,如何下载?
那么问题可以总结如下:
1.查询引擎目前支持的语言语音包(引擎和语言音乐包是绑定的)
2.判断指定Locale是否支持
3.下载语言语音包
接下来分别介绍。
3.1查询引擎目前支持的语言语音包:
1).系统方式
2).使用TextToSpeech方式(指定引擎)
3.1.1系统方式:
分为两部,1.发送Intent给系统 2.在当前activity中接收和解析结果
发送Intent给系统
Intent checkIntent = new Intent();
checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
checkIntent.setPackage(engine);
activity.startActivityForResult(checkIntent, CHECK_REQUEST_CODE);
在当前activity中接收和解析结果(在onActivityResult中调用下面的方法获取Locale列表)
public List<Locale> checkSupportTTSLocale(int requestCode, int resultCode, Intent data) {
ArrayList<Locale> locales = new ArrayList<>();
if (requestCode == CHECK_REQUEST_CODE && resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS && data != null) {
ArrayList<String> availableVoices = data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
if (availableVoices != null && availableVoices.size() > 0) {
for (String voice: availableVoices) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
String[] split = voice.split("-");
if (split.length > 0) {
Locale locale = split.length == 1 ? new Locale(split[0]) : (split.length == 2 ? new Locale(split[0], split[1]): new Locale(split[0], split[1], split[2]));
locales.add(locale);
}
}
}
}
}
return locales;
}
3.1.2使用TextToSpeech方式
public List<Locale> getSupportLocales() {
ArrayList<Locale> locales = new ArrayList<>();
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
Set<Voice> voiceSet = textToSpeech.getVoices();
for (Voice voice: voiceSet) {
locales.add(voice.getLocale());
}
}
return locales;
}
重要:前提是textToSpeech指定了引擎,如textToSpeech = new TextToSpeech(context, listener, “com.google.android.tts”),查询到是“com.google.android.tts”支持的语音语音包对应的Locale列表。
3.2 判断指定Locale是否支持
通过之前的描述应该了解,Locale在不同引擎支持的情况不一样,且对应的语言语音包和引擎是绑定的,所以判断是否支持Locale可以通过TextToSpeech进行判断,通过下面两个方法都可以判断:
TextToSpeech.setLanguage(Locale)
TextToSpeech.isLanguageAvailable(Locale)
3.3 下载语言语音包
当引擎暂不支持指定语言音乐包时,可以通过下载语言音乐包进行支持(若没有语言语音包就没办法了,不过google支持还是挺全的,其它引擎就不一定了)。
使用google系统引擎下载语言音乐包实现如下:
Intent installIntent = new Intent();
installIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
installIntent.setPackage(engine);
activity.startActivityForResult(installIntent, INSTALL_REQUEST_CODE);
在系统页面下载完成之后,返回当前activity后,在onActivityResult中并不能查询到安装结果,还是需要3.1方式进行查询。
四、回调和部分方法
1.TextToSpeech.setOnUtteranceProgressListener(UtteranceProgressListener listener)
UtteranceProgressListener
//API 15
-public abstract void onStart(String utteranceId)
//API 15
-public abstract void onDone(String utteranceId)
//API 15
-public abstract void onError(String utteranceId)
//API 23
-public void onStop(String utteranceId, boolean interrupted)
//API 24
-public void onBeginSynthesis(String utteranceId, int sampleRateInHz, int audioFormat, int channelCount)
//API 24
-public void onAudioAvailable(String utteranceId, byte[] audio)
//API 26
-public void onRangeStart(String utteranceId, int start, int end, int frame)
虽然回调方法不少,但如果是使用google引擎则注意兼容版本(国内实现的引擎应该比较完善,目前看到华为是支持类似onRangeStart的方法,这个方法能够实现实时单词高亮效果等)
2.设置语音属性
//设置音调高低,函数没有明确说明范围,我测试的范围0.5-3.0
setPitch(float pitch)
//设置语速,函数没有明确说明范围,我测试的范围0.5-3.0
setSpeechRate(float speechRate)
这两个方法调用并不是实时生效,下次调用speak时才会生效
3.周期方法
//设置文本给引擎进行播报
speek
//停止语音播报
stop
//关闭引擎
shutdown
没有pause和resume,如果自己实现,建议在speek时进行分句操作。
五、总结
下面对调用进行总结:
1.查询引擎列表,使用需要的引擎实例化TextToSpeech
2.查询语言语音列表,如果不支持,可以通过系统方法(三方实现的调用三方下载语言语音包方法)进行安装
3.设置语言语音包给引擎
4.使用speak进行语音播报,可以在对应的回调中处理逻辑,但要注意支持API版本
5.使用完成时候记得shutdown释放资源
赋大概的时序图如下:
下面是参考的文档和可以参考的资源:
参考:
Android Speech之TTS(文本到语音)源码及流程分析_奔腾的小马-CSDN博客
TTS源码解析_zhupumao的博客-CSDN博客_tts文件解析错误
google例子:
https://android.googlesource.com/platform/development/+/master/samples/TtsEngine?autodive=0
github上高星地址:
https://github.com/ak1394/react-native-tts
https://github.com/gotev/android-speech
https://github.com/HMS-Core/hms-ml-demo
https://github.com/happyalu/Flite-TTS-Engine-for-Android
注:如有错误或探讨的地方,欢迎大家留言哈。