天天看点

TTS源码解析1、TTS初始化2、TTS的调用

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

1、TTS初始化

首先来看TextToSpeech的构造函数

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

紧接着执行initTts操作,初始化TTS:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

首先连接到用户请求的tts引擎服务,接着是默认引擎,最后是高性能引擎,从代码可以看出高性能引擎优先级最高,默认引擎其次。连接代码如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

Engine.INTENT_ACTION_TTS_SERVICE的值为"android.intent.action.TTS_SERVICE";其连接到的服务为action为"android.intent.action.TTS_SERVICE"的服务,在external\svox\pico目录中的AndroidManifest.xml文件可以发现:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

所以可以得到这里连接到的服务就是PicoService,其具体代码如下:其继承于CompatTtsService。

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

我们再来看看CompatTtsService这个类,这个类为抽象类,它的父类为TextToSpeechService,其有一个成员SynthProxy类,该类负责调用TTS的C++层代码。

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

我们来看看CompatTtsService的onCreate()方法,该方法中主要对SynthProxy进行了初始化

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

我们紧接着看看SynthProxy的构造函数都干了什么,我也不知道干了什么,但是里面有个静态代码块,其加载了ttscompat动态库,所以它肯定只是一个代理,实际功能由C++本地方法实现

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

在构造函数中,调用了native_setup方法来初始化引擎,其实现在C++层(com_android_tts_compat_SynthProxy.cpp)。代码如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

上面红线init方法在com_svox_picottsengine.cpp中:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

至此,TTS引擎的初始化就完成了,接下来分析TTS的调用。

2、TTS的调用

我们在应用层一般调用TextToSpeech中的speak()方法,我们来看看其执行流程:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

接着调用runAction():

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

然后调用mServiceConnection中的runAction方法,其具体代码如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

可以发现最后会回调action.run(mService)方法。接着执行service.playAudio();这里的service为PicoService,其继承于抽象类CompatTtsService,而CompatTtsService继承于抽象类TextToSpeechService.所以会执行TextToSpeechService中的playAudio(),该方法位于TextToSpeechService中mBinder中。该方法如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

接着执行mSynthHandler.enqueueSpeechItem(queueMode, item),其代码如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

我们主要看speechItem.play()该方法在Speechitem类中,其为TextToSpeechService内部类

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

在TextToSpeechService中的playAudio()中代码可以知道这里的speechitem为SynthesisSpeechItemV1。因此在play中执行的playimpl()方法为SynthesisSpeechItemV1类中的playimpl()方法,其代码如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

在playImpl方法中会执行onSynthesizeText方法,这是个抽象方法,记住其传递了一个synthesisCallback,后面会讲到。哪该方法具体实现是在哪里呢,没错,就是在TextToSpeechService的子类CompatTtsService中。来看看它怎么实现的:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

mNativeSynth为SynthProxy的一个对象,之前说了他是一个代理类,他的speak方法如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

看到没?什么都没做,就是调用native_speak方法,看它的名字就知道是一个本地方法,那么该方法又是在哪实现呢?就是在com_android_tts_compat_SynthProxy.cpp中,该文件中有下面的映射数组(只截取了一部分):

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

java中对应的native_speak方法对应的C++层代码方法为com_android_tts_compat_SynthProxy_speak方法,接下来来看看该方法是怎么实现的:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

该方法中调用了TTS引擎中的synthesizeText方法,那么该方法是怎么实现的呢?该方法位于com_svox_picottsengine.cpp中,主要代码(代码太长,只贴部分)如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

其中picoSynthDoneCBPtr()为回调函数,其为TTS初始化过程传入的:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

而TTS的init初始化在native_setup中被调用

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

那么__ttsSynthDoneCB又是什么呢?其实现如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

callRequestAudioAvailable()方法如下:

TTS源码解析1、TTS初始化2、TTS的调用
TTS源码解析1、TTS初始化2、TTS的调用

这个地方调用env->CallIntMethod()方法,在C++层调用java方法,那么这个方法在哪呢?这里的env就是之前说的synthesisCallback,synthesisCallback为PlaybackSynthesisCallback类的对象,那么将会执行PlaybackSynthesisCallback中的audioAvailable()

TTS源码解析1、TTS初始化2、TTS的调用

至此,TTS的调用就结束了。

继续阅读