正经人,雾凇,胜利精密-第十视角,围观中美贸易新动向

admin 4个月前 ( 06-17 04:33 ) 0条评论
摘要: 解读 Android TTS 语音合成播报...

跟着从事 Android 开发年限添加,担任的作业项目也从运用层开发逐渐过渡到 Android Framework 层开发。尽管一开端就知道 Android 常识体系的巨大,可是当你逐渐从 Application 层向 Framework 层走的时分,你才发现之前懂得认知真是太少。之前更多打交道的 Activity 和 Fragment ,关于 Service 和 Broadcast 触及的很少,更多重视的是界面的布局、动画、网络恳求等,尽管走运用开发的话,后期会重视架构、功用优化、Hybrid等,可是逐渐触摸 Framework 层相关模块时分,发现里边的常识点各种扑朔迷离,就比如讲讲今日共享的主题是 Android TTS

话不多说,先来张图,共享纲要如下:

纲要

之前受一篇文章启示,说的是怎样解说好一个技能点常识,能够分为两部分去介绍:外部运用维度和内部规划维度,根本从这两个视点动身,能够把一个技能点讲的透彻。相同,我把这种办法运用到写作中去。

外部运用维度什么是 TTS

在 Android 中,TTS全称叫做 Text to Speech,从字面就能了解它处理的问题是什么,把文本转为语音服务,意思便是你输入一段文本信息,然后Android 体系能够把这段文字播报出来。这种运用场景现在比较多是在各种语音帮手APP上,许多手机体系集成商内部都有内置文本转语音服务,能够读当时页面上的文本信息。相同,在一些阅览类APP上咱们也能看到相关服务,翻开微信读书,里边就直接能够把当时页面直接用语音办法播映出来,特别合适哪种不方便拿着手机屏幕阅览的场景。

TTS 技能规范

这儿首要用到的是TextToSpeech类来完结,运用TextToSpeech的进程如下:

创立TextToSpeech方针,创立时传入OnInitListener监听器监听演示创立成功。

设置TextToSpeech所运用言语国家选项,经过回来值判别TTS是否支撑该言语、国家选项。

调用speak或synthesizeToFile办法。

封闭TTS,收回资源。

XML文件

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent">

<ScrollView

android:layout_width="match_parent"

android:layout_height="match_parent">

<LinearLayout

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical">

<EditText

android:id="@+id/edit_text1"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="杭州自秦朝设县治以来已有2200多年的前史,曾是吴越国和南宋的国都。因风景秀丽,素有“人间天堂”的美誉。杭州得益于京杭运河和通商口岸的便当,以及本身兴旺的丝绸和粮食工业,前史上曾是重要的商业集散中心。"/>

<Button

android:id="@+id/btn_tts1"

android:layout_width="150dp"

android:layout_height="60dp"

android:layout_marginTop="10dp"

android:text="TTS1"/>

<EditText

android:id="@+id/edit_text2"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:text="伊利揭露告发原创始人郑俊怀:多名高官充任保护伞 北京青年报 2018-10-24 12:01:46   10月24日上午,伊利公司在企业官方网站宣布告发信,揭露举龙珠h报郑俊怀等人,宣称郑俊怀索要巨额违法所得不成,动用最高检某原副检察长等人施压,长时刻诽谤虐待伊利,多位省部级、厅局级领导均充任郑俊怀保护伞,人为抹掉2.4亿违法事实,运作假弛刑,14年来无人敢处理。"/>

<Button

android:id="@+id/btn_tts2"

android:layout_width="150dp"

android:layout_height="60dp"

android:layout_marginTop="10dp"

android:text="TTS2"/>

<Button

android:id="@+id/btn_cycle"

android:layout_width="150dp"

android:layout_height="60dp"

android:layout_marginTop="10dp"

android:text="Cycle TTS"/>

<Button

android:id="@+id/btn_second"

android:layout_width="150dp"

android:layout_height="60dp"

android:layout_marginTop="10dp"

android:text="Second TTS"/>

</LinearLayout>

</ScrollView>

</RelativeLayout>

Activity文件

publicclassTtsMainActivityextendsAppCompatActivityimplementsView.OnClickListener,TextToSpeech.OnInitListener{

privatestaticfinalString TAG = TtsMainActivity.class.getSimpleName;

privatestaticfinalintTHREADNUM = 100; // 测验用的线程数目

priv小世界gogogoateEditText mTestEt1;

privateEditText mTestEt2;

privateTextToSpeech mTTS; // TTS方针

privateXKAudioPolicyManager mXKAudioPolicyManager;

privateHashMap mParams = null;

@Override

protectedvoidonCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mTestEt1 = (EditText) findViewById(R.id.edit_text1);

mTestEt2 = (EditText) findViewById(R.id.edit_text2);

findViewById(R.id.btn_tts1).setOnClickListener( this);

findViewById(R.id.btn_tts2).setOnClickListener( this);

findViewById(R.id.btn_cycle).setOnClickListener( this);

findViewById(R.id.btn_second).setOnClickListener( this);

init;

}

privatevoidinit{

mTTS = newTextToSpeech( this.getApplicationContext, this);

mXKAudioPolicyManager = XKAudioPolicyManager.getInstance( this.getApplication);

mParams = newHashMap;

mParams.put正经人,雾凇,成功精细-第十视角,围观中美交易新动向(TextToSpeech.Engine.KEY_PARAM_STREAM, "3"); //设置播映类型(音频流类型)

}

@Override

publicvoidonInit(intstatus){

if(status == TextToSpeech.SUCCESS) {

intresult寿竹根的成效与效果 = mTTS.setLanguage(Locale.ENGLISH);

if(result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_正经人,雾凇,成功精细-第十视角,围观中美交易新动向NOT_SUPPORTED) {

Toast.makeText( this, "数据丢掉或不支撑", Toast.LENGTH_SHORT).show;

}

}

}

@Override

publicvoidonClick(View v){

intid = v.getId;

switch(id){

caseR.id.btn_tts1:

TtsPlay1;

break;

caseR.id.btn_tts2:

TtsPlay2;

break;

caseR.id.btn_second:

TtsSecond;

break;

caseR.id.btn_cycle:

TtsCycle;

break;

default:

break;

}

}

privatevoidTtsPlay1{

if(mTTS != null&& !mTTS.isSpeaking && mXKAudioPolicyManager.requestAudioSource) {

//mTTS.setOnUtteranceProgressListener(new ttsPlayOne);

String text1 = mTestEt1.getText.toString;

Log.d(TAG, "TtsPlay1-----------播映文本内容:"+ text1);

//朗诵,留意这儿三个参数的added in API level 4 四个参数的added in API level 21

mTTS.speak(text1, TextToSpeech.QUEUE_FLUSH, mParams正经人,雾凇,成功精细-第十视角,围观中美交易新动向);

}

}

privatevoidTtsPlay2{

if(mTTS != null&& !mTTS.isSpeaking && mXKAudioPolicyManager.requestAudioSource) {

//mTTS.setOnUtteranceProgressListener(new ttsPlaySecond);

String text2 = mTestEt2.getText.toString;

Log.d(TAG, "TtsPlay2-----------播映文本内容:"+ text2);

// 设置腔调,值越大声响越尖(女生),值越小则变成男声,1奇书色医.0是惯例

mTTS.setPitch( 0.8f);

//设定语速 ,默许1.0正常语速

mTTS.setSpeechRate( 1f);

//朗诵,留意这儿三个参数的added in API level 4 四个参数的added in API level 21

mTTS.speak(text2, TextToSpeech.QUEUE_FLUSH, mParams);

}

}

privatevoidTtsSecond{

Intent intent = newIntent(TtsMainActivity. this,TtsSecondAcitivity.class);

startActivity(intent);

}

privatevoidTtsCycle{

longmillis1 = System.currentTimeMillis;

for( inti = 0; i < THREADNUM; i++) {

Thread tempThread = newThread( newMyRunnabl多洛斯级大型运送空母e(i, THREADNUM));

tempThread.setName( "线程"+ i);

tempThread.start;

}

longmillis2 = System.currentTimeMillis;

Log.d(TAG, "循环测验发音消耗时刻:"+ (millis2 - millis1));

}

@Override

protectedvoidonStart{

super.onStart;

}

@Override

protectedvoidonStop{

super.onStop;

}

@Override

protectedvoidonDestroy{

super.onDestroy;

shutDown;

}

privatevoidshutDown{

if(mTTS != null){

mTTS.stop;

mTTS.shutdown;

}

if(mXKAudioPolicyManager != null){

mXKAudioPolicyManager.releaseAudioSource;

}

}

/**

* 自界说线程可履行处理

* */

classMyRunnableimplementsRunnable{

privateinti; // 第几个线程

privateintthreadNum; // 一共创立了几个线程

publicMyRunnable(inti, intthreadNum){

this.i = i;

this.threadNum = threadNum;

}

@Override

publicvoidrun{

runOnUiThread( newRunnable {

@Override

publicvoidrun{

Log.d(TAG, "在主线程中履行index:"+ i + ",线程总数:"+ threadNum);

if(i % 2== 0){

Log.d(TAG, "TtsPlay1 index:"+ i);

TtsPlay1;

}

else{

Log.d(TAG, "TtsPlay2 index:"+ i);

TtsPlay2;

}

try{

Thread.sleep( 10000);

} catch(InterruptedException e) {

e.printStackTrace;

}

}

});

}

}

publicclassttsPlayOneextendsUtteranceProgressListener{

@Override

publicvoidonStart(String utteranceId){

Log.d(TAG, "ttsPlayOne-----------onStart");

}

@Override

publicvoidonDone(String utteranceId){

Log.d(TAG, "ttsPlayOne-----------onDone");

}

@Override

publicvoid(String utteranceId){

Log.d(TAG, "ttsPlayOne-----------");

}

}

publicclassttsPlaySecondextendsUtteranceProgressListener{

@Override

publicmc锁哥voidonStart(String utteranceId){

Log.d(TAG, "ttsPlaySecond-----------onStart");

}

@Override

publicvoidonDone(String utteranceId){

Log.d(TAG, "ttsPlaySecond-----------onDone");

}

@Override

publicvoid(String utteranceId){

Log.d(TAG, "ttsPlaySecond-----------");

}

}

}

加上权限

<uses-permissionandroid:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>

<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

TTS 最佳实践

由于现在我在公司担任开发的产品是归于语音帮手类型,天然这类 TTS 发声的问题和坑日常见的比较多。常见的有如下几种类型:

跟着物联网的到来,IoT设备增多,那么关于相似语音帮手相关运用也会增多,由于语音是一个很好的进口,现在逐渐从显现到去显现的进程,许多智能设备有些是不需求屏幕的,只需求能辨认语音和播映声响。因而,跟着这类运用的增加,关于TTS 相关的API接口调用频率必定也是加大,信任谷歌在这方面也会逐渐在完善。

内部规划维度

从外部运用视点下手,根本是了解API接口和详细项目中运用碰到的问题,然后不断总结出来比较优化的实践办法。了解完外部视点切入,那么咱们需求里边内部规划是怎样一回事,究竟作为一个开发者,知道详细完成原理是一个根本功。

处理方针

Android TTS 方针便是处理文本转化为语音播报的进程。那它到底是怎样完成的呢,咱们从TextToSpeech类的结构函数开端剖析。

这儿咱们用Android 6.0版别源码剖析为主,首要触及的相关类和接口文件,在源码中的方位如下:

frameworkbasecorejavaandroidspeechttsTextToSpeech.java

frameworkbase/corejava/androidspeechttsTextToSpeechService.java

externalsvoxpicosrccomsvoxpicoPicoService.java

externalsvoxpicocompatsrccomandroidttscompatCompatTtsService.java

externalsvoxpicocompatsrccomandroidttscompatSynthProxy.java

externalsvoxpicocompatjnicom_android_tts_compat_SynthProxy.cpp

externalsvoxpicottscom_svox_picottsengine.cpp

dfe008

初始化视点:先看TextToSpeech类,在运用时,一般TextToSpeech类要进行初始化,它的结构函数有三个,终究真实调用的结构函数代码如下:

/**

* Used by the framework to instantiate TextToSpeech objects with a supplied

* package name, instead of using {@linkandroid.content.Context#getPackageName}

*

* @hide

*/

publicTextToSpeech(Context context, OnInitListener listener, String engine,

String packageName, booleanuseFallback){

mContext = context;

mInitListener = listener;

mRequestedEngine = engine;

mUseFallback = useFallback;

mEarcons = newHashMap<String, Uri>;

mUtterances = newHashMap<CharSequence, Uri>;

mUtteranceProgressListener = null;

mEnginesHelper = newTtsEngines(mContext);

initTts;

}

从结构函数能够看到,调用到initTts操作,咱们看下initTts办法里是什么东东,代码如下:

privateintinitTts{

// Step 1: Try connecting to the engine that was requested.

if(mRequestedEngine != null) {

if(mEnginesHelper.isEngineInstalled(mRequestedEngine)) {

if(connectToEngine(mRequestedEngine)) {

mCurrentEngine 合肥丝足会所= mRequestedEngine;

returnSUCCESS;

} elseif(!mUseFallback) {

mCurrentEngine = null;

dispatchOnInit(ERROR);

returnERROR;

}

} elseif(!mUseFallback) {

Log.i(TAG, "R正经人,雾凇,成功精细-第十视角,围观中美交易新动向equested engine not installed: "+ mRequestedEngine);

mCurrentEngine = null;

dispatchOnInit(ERROR);

returnERROR;

}

}

// Step 2: Try connecting to the user's default engine.

finalString defaultEngine = getDefaultEngine;

if(defaultEngine != null&& !defaultEngine.equals(mRequestedEngine)) {

if(connectToEngine(defaultEngine)) {

mCurrentEngine = d俞秋言efaultEngine;

returnSUCCESS;

}

}

// Step 3: Try connecting to the highest ranked engine in the

// system.

finalString highestRanked = mEnginesHelper.getHighestRankedEngineName;

if(highestRanked != null&& !highestRanked.equals(mRequestedEngine) &&

!highestRanked.equals(defaultEngine)) {

if(connectToEngine(highestRanked)) {

mCurrentEngine = highestRanked;

returnSUCCESS;

}

}

// NOTE:The API currently does not allow the calle正经人,雾凇,成功精细-第十视角,围观中美交易新动向r to query whether

// they are actually connected to any engine. Th创盟易购is might fail for various

// reasons like if the user disables all her TTS engines.

mCurrentEngine = null;

dispatchOnInit(ERROR);

returnERROR;

}

这儿比较有意思了,第一步先去衔接用户恳求的TTS引擎服务(这儿能够让咱们自界说TTS引擎,能够替换体系默许的引擎),假如没找到衔接用户的TTS引擎,那么就去衔接默许引擎,终究是衔接高功用引擎,从代码能够看出高功用引擎优先级最高,默许引擎其次,connectToEngine办法代码如下:

privatebooleanconnectToEngine(String engine){

Connection connection = newConnection;

Intent intent = newIntent(Engine.INTENT_ACTION_TTS_SERVICE);

intent.setPackage(engine);

booleanbound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);

if(!bound) {

Log.e(TAG, "Failed to bind to "+ engine);

returnfalse;

} else{

Log.i(TAG, "Sucessfully bound to "+ engine);

mConnectingServiceConnection = connection;

returntrue;

}

}

这儿的Engine.INTENT_ACTION_TTS_SERVICE的值为"android.intent.action.TTS_SERVICE";其衔接到的服务为action,为"android.intent.action.TTS_SERVICE"的服务,在externalsvoxpico目录中的AndroidManifest.xml文件能够发现:

<serviceandroid:name=".PicoService"

android:label="@string/app_name">

<intent-filter>

<actionandroid:name="android.intent.action.TTS_SERVICE"/>

<categoryandroid:name="android.intent.category.DEFAULT"/>

</intent-filter>

<meta-dataandroid:name="android.speech.tts"android:resource="@xml/tts_engine"/>

</service>

体系自带的默许衔接的服务叫做PicoService,其详细代码如下:其承继于CompatTtsService。

publicclassPicoServiceextendsCompatTtsService{

privatestaticfinalString TAG = "PicoService";

@Override

protectedString getSoFilename{

return"libttspico.so";

}

}

咱们再来看看CompatTtsService这个类,这个类为笼统类,它的父类为TextToSpeechService,其有一个成员SynthProxy类,该类担任调用TTS的C++层代码。如图:

CompatTtsService代码

咱们来看看CompatTtsService的onCreate办法,该办法中首要对SynthProxy进行了初始化:

@Override

publicvoidonCreate{

if(DBG) Log.d(TAG, "onCreate");

String soFilename = getSoFilename;

if(mNativeSynth != null) {

mNativeSynth.stopSync;

mNativeSynth.shutdown;

mNativeSynth = null;

}

// Load the engineConfig from the plugin if it has any special configuration

// to be loaded. By convention, if an engine wants the TTS framework to pass

// in any configuration, it must put it into its content provider which has the URI:

// content://<packageName>.providers.SettingsProvider

// That content provider must provide a Cursor which returns the String that

// is to be passed back to the native .so file for the plugin when getString(0) is

// called on it.

// Note that the TTS framework does not care what this String data is: it is something

// that comes from the engine plugin and is consumed only by the engine plugin itself.

String engineConfig = "";

Cursor c = getContentResolver.query(Uri.parse( "content://"+ getPackageName

+ ".providers.SettingsProvider"), null, null, null, null);

if(c != null){

c.moveToFirst;

engineConfig = c.getString( 0);

c.close;

}

mNativeSynth = newSynthProxy(soFilename, engineConfig);

// mNativeSynth is used by TextToSpeechService#onCreate so it must be set prior

// to that call.

// getContentResolver is also moved prior to super.onCreate, and it works

// because the super method don't sets a field or value that affects getContentResolver;

// (including the content resolver itself).

super.onCreate;

}

紧接着看看SynthProxy的结构函数都干了什么,我也不知道干了什么,可是里边有个静态代码块,其加载了ttscompat动态库,所以它必定仅仅一个署理,实践功用由C++本地办法完成

/**

* Constructor; pass the location of the native TTS .so to use.

*/

publicSynthProxy(String nativeSoLib, String engineConfig){

booleanapplyFilter = shouldApplyAudioFilter(nativeSoLib);

Log.v(TAG, "About to load "+ nativeSoLib + ", applyFilter="+ applyFilter);

mJniData = native_setup(nativeSoLib, engineConfig);

if(mJniData == 0) {

thrownewRuntimeException( "Failed to load "+ nativeSoLib);

}

native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,

PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);

}

咱们能够看到,在结构函数中,调用了native_setup办法来初始化引擎,其完成在C++层(com_android_tts_compat_SynthProxy.cpp)。

nativeSetup代码

咱们能够看到ngine->funcs->init(engine, __ttsSynthDoneCB, engConfigString);这句代码比较要害,这个init办法上面在com_svox_picottsengine.cpp中,如下:

/* Google Engine API function implementations */

/** init

* Allocates Pico memory block and initializes the Pico system.

* synthDoneCBPtr - Pointer to callback function which will receive generated samples

* config - the engine configuration parameters, here only contains the non-system path

* for the lingware location

* return tts_result

*/

tts_result TtsEngine::init( synthDoneCB_t synthDoneCBPtr, constchar*config )

{

if(synthDoneCBPtr == NULL) {

ALOGE( "Callback pointer is NULL");

returnTTS_FAILURE;

}

picoMemArea = malloc( PICO_MEM_SIZE );

if(!picoMemArea) {

ALOGE( "Failed to allocate memory for Pico system");

returnTTS_FAILURE;

}

pico_Status ret = pico_initialize( picoMemArea, PICO_MEM_SIZE, &picoSystem );

if(PICO_OK != ret) {

ALOGE( "Failed to initialize Pico system");

free( picoMemArea );

picoMemArea = NULL;

returnTTS_FAILURE;

}

picoSynthDoneCBPtr = synthDoneCBPtr;

picoCurrentLangIndex = -1;

// was the initialization given an alternative path for the lingware location?

if((config != NULL) && ( strlen(config) > 0)) {

pico_alt_lingware_path = ( char*) malloc( strlen(config));

strcpy(( char*)pico_alt_lingware_path, config);

ALOGV( "Alternative lingware path %s", pico_alt_lingware_path);

} else{

pico_alt_lingware_path = ( char*) malloc( strlen(PICO_LINGWARE_PATH) + 1);

strcpy(( char*)pico_alt_lingware_path, PICO_LINGWARE_PATH);

ALOGV( "Using predefined lingware path %s", pico_alt_lingware_path);

}

returnTTS_SUCCESS;

}

到这儿,TTS引擎的初始化就完结了。

再看下TTS调用的视点,一般TTS调用的类是TextToSpeech中的speak办法,咱们来看看其履行流程:

publicintspeak(finalCharSequence text,

finalintqueueMode,

finalBundle params,

finalString utteranceId){

returnrunAction( newAction<Integer> {

@Override

publicInteger run(ITextToSpeechService service)throwsRemoteException {

Uri utteranceUri = mUtterances.get(text);

if(utteranceUri != null) {

returnservice.playAudio(getCallerIdentity, utteranceUri, queueMode,

getParams(params), utteranceId);

} else{

returnservice.speak(getCallerIdentity, text, queueMode, getParams(params),

utteranceId);

}

}

}, ERROR, "speak");

}

首要是看runAction办法:

private<R> R runAction(Action<R> action, R errorResult, String method,

booleanreconnect, booleanonlyEstablishedConnection){

synchronized(mStartLock) {

if(mServiceConnection == null) {

Log.w(TAG, method + " failed: not bound to TTS engine");

returnerrorResult;

}

returnmServiceConnection.runAction(action, errorResult, method, reconnect,

onlyEstablishedConnection);

}

}

首要看下mServiceConnection类的runAction办法,

public<R> R runAction(Action<R> action, R errorResult, String method,

booleanreconnect, booleanonlyEstablishedConnection){

synchronized(mStartLock) {

try{

if(mService == null) {

Log.w(TAG, method + " failed: not connected to TTS engine");

returner女囚吧rorResult;

}

if(onlyEstablishedConnection && !isEstablished) {

Log.w(TAG, method + " failed: TTS engine connection not fully set up");

returnerrorResult;

}

returnaction.run(mService);

} catch(RemoteException ex) {

Log.e(TAG, method + " failed", ex);

if(reconnect) {

disconnect;

initTts;

}

returnerrorResult;

}

}

}

能够发现终究会回调action.run(mService)办法。接着履行service.playAudio,这儿的service为PicoService,其承继于笼统类CompatTtsService,而CompatTtsService承继于笼统类TextToSpeechService。

所以会履行TextToSpeechService中的playAudio,该办法坐落TextToSpeechService中mBinder中。该办法如下:

@Override

publicintplayAudio(IBinder caller, Uri audioUri, intqueueMode, Bundle params,

String utteranceId){

if(!checkNonNull(caller, audioUri, params)) {

returnTextToSpeech.ERROR;

}

SpeechItem item = newAudioSpeechItemV1(caller,

Binder.getCallingUid, Binder.getCallingPid, params, utteranceId, audioUri);

returnmSynthHandler.enqueueSpeechItem(queueMode, item);

}

接着履行mSynthHandler.enqueueSpeechItem(queueMode, item),其代码芳华而立如下:

/**

* Adds a speech item to the queue.

*

* Called on a service binder thread.

*/

publicintenqueueSpeechItem(intqueueMode, finalSpeechItem speechItem){

UtteranceProg正经人,雾凇,成功精细-第十视角,围观中美交易新动向ressDispatcher utterenceProgress = null;

if(speechItem instanceofUtteranceProgressDispatcher) {

utterenceProgress = (UtteranceProgressDispatcher) speechItem;

}

if(!speechItem.isValid) {

if(utterenceProgress != null) {

utterenceProgress.dispatch(

TextToSpeech.ERROR_INVALID_REQUEST);

}

returnTextToSpeech.ERROR;

}

if(queueMode == TextToSpeech.QUEUE_FLUSH) {

stopForApp(speechItem.getCallerIdentity);

} elseif(queueMode == TextToSpeech.QUEUE_DESTROY) {

stopAll;

}

Runnable runnable = newRunnable {

@Override

publicvoidrun{

if(isFlushed(speechItem)) {

speechItem.stop;

} else{

setCurrentSpeechItem(speechItem);

speechItem.pl蛇窟迷情ay;

setCurrentSpeechItem( null);

}

}

};

Message msg = Message.obtain( this, runnable);

// The obj is used to remove all callbacks from the given app in

// stopForApp(String).

//

// Note that this string is interned, so the == comparison works.

msg.obj = speechItem.getCallerIdentity;

if(sendMessage(msg)) {

returnTextToSpeech.SUCCESS;

} else{

Log.w(TAG, "SynthThread has quit");

if(utterenceProgress != null) {

utterenceProgress.dispatch(TextToSpeech.ERROR_SERVICE);

}

returnTextToSpeech.ERROR;

}

}

首要是看 speechItem.play办法,代码如下:

/**

* Plays the speech item. Blocks until playback is finished.

* Muleisimaost not be called more than once.

*

* Only called on the synthesis thread.

*/

publicvoidplay{

synchronized( this) {

if(mStarted) {

thrownewIllegalStateException( "play called twice");

}

mStarted = true;

}

playImpl;

}

protectedabstractvoidplayImpl;

能够看到首要播映完成办法为playImpl,那么在TextToSpeechService中的playAudio中代码能够知道这儿的speechitem为SynthesisSpeechItemV1。

因而在play中履行的playimpl办法为SynthesisSpeechItemV1类中的playimpl办法,其代码如下:

@Override

protectedvoidplayImpl{

AbstractSynthesisCallback synthesisCallback;

mEventLogger.onRequestProcessingStart;

synchronized( this) {

// stop might have been called before we enter this

// synchronized b栾立平lock.

if(i手艺坊时髦清凉织造sStopped) {

return;

}

mSynthesisCallback = createSynthesisCallback;

synthesisCallback = mSynthesisCallback;

}

TextToSpeechService. this.onSynthesizeText(mSynthesisRequest, synthesisCallback);

// Fix for case where client called .start & .error, but did not called .done

if(synthesisCallback.hasStarted && !synthesisCallback.hasFinished) {

synthesisCallback.done;

}

}

在playImpl办法中会履行onSynthesizeText办法,这是个笼统办法,记住其传递了一个synthesisCallback,后边会讲到。哪该办法详细完成是在哪里呢,没错,便是在TextToSpeechService的子类CompatTtsService中。来看看它怎样完成的:

@Override

protectedvoidonSynthesizeText(SynthesisRequest request, SynthesisCallback callback){

if(mNativeSynth == null) {

callback.error;

return;

}

// Set language

String lang = request.getLanguage;

String country = request.getCountry;

String variant = request.getVariant;

if(mNativeSynth.setLanguage(lang, country, variant) != TextToSpeech.SUCCESS) {

Log.e(TAG, "setLanguage("+ lang + ","+ country + ","+ variant + ") failed");

callback.error;

return正经人,雾凇,成功精细-第十视角,围观中美交易新动向;

}

// Set speech rate

intspeechRate = request.getSpeechRate;

if(mNativeSynth.setSpeechRate(speechRate) != TextToSpeech.SUCCESS) {

Log.e(TAG, "setSpeechRate("+ speechRate + ") failed");

callback.error;

return;

}

// Set speech

intpitch = request.getPitch;

if(mNativeSynth.setPitch(暗夜恩惠录pitch) != TextToSpeech.SUCCESS) {

Log.e(TAG, "setPitch("+ pitch + ") failed");

callback.error;

return;

}

// Synthesize

if(mNativeSynth.speak(request, callback) != TextToSpeech.SUCCESS) {

callback.error;

return;

}

}

终究又回到体系供给的pico引擎中,在com_android_tts_compat_SynthProxy.cpp这个文件中,能够看到运用speak办法,代码如下:

staticjint

com_android_tts_compat_SynthProxy_speak(JNIEnv *env, jobject thiz, jlong jniData,

jstring textJavaString, jobject requ烈玉锵est)

{

SynthProxyJniStorage* pSynthData = getSynthData(jniData);

if(pSynthData == NULL) {

returnANDROID_TTS_FAILURE;

}

i我的爱皇亲国戚nitializeFilter;

Mutex:: Autolock l(engineMutex);

android_tts_engine_t*engine = pSynthData->mEngine;

if(!engine) {

returnANDROID_TTS_FAILURE;

}

SynthRequestData *pRequestData = newSynthRequestData;

pRequestData->jniStorage = pSynthData;

pRequestData->env = env;

pRequestData->request = env->NewGlobalRef(request);

pRequestData->startCalled = false;

constchar*textNativeString = env->GetStringUTFChars(textJavaString, 0);

memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);

intresult = engine->funcs->synthesizeText(engine, textNativeString,

pSynthData->mBuffer, pSynthData->mBufferSize, static_cast< void*>(pRequestData));

env->ReleaseStringUTFChars(textJavaString, textNativeString);

return(jint) result;

}

至此,TTS的调用就完毕了。

TTS 优下风

从完成原理咱们能够看到Android体系原生自带了一个TTS引擎。那么在此,咱们就也能够去自界说TTS引擎,只要承继ITextToSpeechService接口即可,完成里边的办法。这就为后续自界说TTS引擎埋下伏笔了,由于体系默许的TTS引擎是不支撑中文,那么市场上比较好的TTS相关产品,一般是集成讯飞或许Nuance等第三方供货商。

因而,咱们也能够看到TTS优下风。

优势:接口界说完善,有着完好的API接口办法,一起支撑扩展,可根据本身开发事务需求从头打造TTS引擎,而且与原生接口做兼容,可适配。

下风:原生体系TTS引擎支撑的多国言语有限,现在不支撑多实例和多通道。

演进趋势

从现在来看,跟着语音成为更多Iot设备的进口,那么在语音TTS组成播报方面技能会越来越老练,特别是关于Android 体系原生相关的接口也会越来越强壮。因而,关于TTS后续的开展,应该是冉冉上升。

小结

总的来说,关于一个常识点,前期经过运用文档介绍,到详细实践,然后在实践中优化进行总结,挑选一个最佳的实践计划。当然不能满意“知其然而不知其所以然”,所以得去看背面的完成原理是什么。这个常识点优下风是什么,在哪些场景比较适用,哪些场景不适用,接下来会演进趋势怎样样。经过这么一整套流程,那么关于一个常识点来说,能够算是了然于胸了。

我们都在看

欢迎前往安卓巴士博客区投稿,技能成善于共享

等待巴友留言,一起讨论学习

声明:该文观念仅代表作者自己,搜狐号系信息发布渠道,搜狐仅供给信息存储空间服务。
文章版权及转载声明:

作者:admin本文地址:http://www.10th-insight.com/articles/1732.html发布于 4个月前 ( 06-17 04:33 )
文章转载或复制请以超链接形式并注明出处第十视角,围观中美贸易新动向