TTS全称为“TextToSpeech”,是Android原生在文本转语音服务。本文将从TTS使用过程,对TTS源码进行分析。
主要涉及的源码有:
framework\base\core\java\android\speech\tts\TextToSpeech.java
framework\base/core\java/android\speech\tts\TextToSpeechService.java
external\svox\pico\src\com\svox\pico\PicoService.java
external\svox\pico\compat\src\com\android\tts\compat\CompatTtsService.java
external\svox\pico\compat\src\com\android\tts\compat\SynthProxy.java
external\svox\pico\compat\jni\com_android_tts_compat_SynthProxy.cpp
external\svox\pico\tts\com_svox_picottsengine.cpp
首先来看TextToSpeech的构造函数
紧接着执行initTts操作,初始化TTS:
首先连接到用户请求的tts引擎服务,接着是默认引擎,最后是高性能引擎,从代码可以看出高性能引擎优先级最高,默认引擎其次。连接代码如下:
Engine.INTENT_ACTION_TTS_SERVICE的值为"android.intent.action.TTS_SERVICE";其连接到的服务为action为"android.intent.action.TTS_SERVICE"的服务,在external\svox\pico目录中的AndroidManifest.xml文件可以发现:
所以可以得到这里连接到的服务就是PicoService,其具体代码如下:其继承于CompatTtsService。
我们再来看看CompatTtsService这个类,这个类为抽象类,它的父类为TextToSpeechService,其有一个成员SynthProxy类,该类负责调用TTS的C++层代码。
我们来看看CompatTtsService的onCreate()方法,该方法中主要对SynthProxy进行了初始化
我们紧接着看看SynthProxy的构造函数都干了什么,我也不知道干了什么,但是里面有个静态代码块,其加载了ttscompat动态库,所以它肯定只是一个代理,实际功能由C++本地方法实现
在构造函数中,调用了native_setup方法来初始化引擎,其实现在C++层(com_android_tts_compat_SynthProxy.cpp)。代码如下:
上面红线init方法在com_svox_picottsengine.cpp中:
至此,TTS引擎的初始化就完成了,接下来分析TTS的调用。
我们在应用层一般调用TextToSpeech中的speak()方法,我们来看看其执行流程:
接着调用runAction():
然后调用mServiceConnection中的runAction方法,其具体代码如下:
可以发现最后会回调action.run(mService)方法。接着执行service.playAudio();这里的service为PicoService,其继承于抽象类CompatTtsService,而CompatTtsService继承于抽象类TextToSpeechService.所以会执行TextToSpeechService中的playAudio(),该方法位于TextToSpeechService中mBinder中。该方法如下:
接着执行mSynthHandler.enqueueSpeechItem(queueMode, item),其代码如下:
我们主要看speechItem.play()该方法在Speechitem类中,其为TextToSpeechService内部类
在TextToSpeechService中的playAudio()中代码可以知道这里的speechitem为SynthesisSpeechItemV1。因此在play中执行的playimpl()方法为SynthesisSpeechItemV1类中的playimpl()方法,其代码如下:
在playImpl方法中会执行onSynthesizeText方法,这是个抽象方法,记住其传递了一个synthesisCallback,后面会讲到。哪该方法具体实现是在哪里呢,没错,就是在TextToSpeechService的子类CompatTtsService中。来看看它怎么实现的:
mNativeSynth为SynthProxy的一个对象,之前说了他是一个代理类,他的speak方法如下:
看到没?什么都没做,就是调用native_speak方法,看它的名字就知道是一个本地方法,那么该方法又是在哪实现呢?就是在com_android_tts_compat_SynthProxy.cpp中,该文件中有下面的映射数组(只截取了一部分):
java中对应的native_speak方法对应的C++层代码方法为com_android_tts_compat_SynthProxy_speak方法,接下来来看看该方法是怎么实现的:
该方法中调用了TTS引擎中的synthesizeText方法,那么该方法是怎么实现的呢?该方法位于com_svox_picottsengine.cpp中,主要代码(代码太长,只贴部分)如下:
其中picoSynthDoneCBPtr()为回调函数,其为TTS初始化过程传入的:
而TTS的init初始化在native_setup中被调用
那么__ttsSynthDoneCB又是什么呢?其实现如下:
callRequestAudioAvailable()方法如下:
这个地方调用env->CallIntMethod()方法,在C++层调用java方法,那么这个方法在哪呢?这里的env就是之前说的synthesisCallback,synthesisCallback为PlaybackSynthesisCallback类的对象,那么将会执行PlaybackSynthesisCallback中的audioAvailable()
至此,TTS的调用就结束了。