public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private TextToSpeech textToSpeech = null;
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.btn);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
textToSpeech = new TextToSpeech(MainActivity.this,
new TextToSpeech.OnInitListener() {
@Override
public void onInit(int i) {
if (i == TextToSpeech.SUCCESS) {
int language = textToSpeech.setLanguage(Locale.CHINESE);
textToSpeech.setSpeechRate(1.2f);
textToSpeech.setPitch(0.7f);
if ((language != textToSpeech.LANG_COUNTRY_AVAILABLE)
&& (language != TextToSpeech.LANG_AVAILABLE)) {
Toast.makeText(MainActivity.this, "暂时不支持这种语言的朗读", Toast.LENGTH_SHORT)
.show();
}
textToSpeech.speak("微信支付到账10元",
TextToSpeech.QUEUE_ADD, null);
}else{
Log.i(TAG, "onInit: TTS引擎初始化失败");
}
}
});
}
});
}
}
如果打印TTS引擎初始化失败,去查看手机“文字转语音(TTS)输出”有没有语音引擎,没有就去下载一个科大讯飞引擎3.0
源码:
首先new TextToSpeech,最终调用5个参数的TextToSpeech()方法,做一些初始化。
public TextToSpeech(Context context, OnInitListener listener, String engine,
String packageName, boolean useFallback) {
mContext = context;
mInitListener = listener;
mRequestedEngine = engine;
mUseFallback = useFallback;
mEarcons = new HashMap<String, Uri>();
mUtterances = new HashMap<CharSequence, Uri>();
mUtteranceProgressListener = null;
mEnginesHelper = new TtsEngines(mContext);
initTts();
}
点击initTts()方法,首先会连接用户指定的TTS引擎,如果没找到就会连接默认的TTS引擎,还是没找到就去连接高性能TTS引擎,都连接不上就调用dispatchOnInit,回调onItit。
private int initTts() {
// Step 1: Try connecting to the engine that was requested.
if (mRequestedEngine != null) {
if (mEnginesHelper.isEngineInstalled(mRequestedEngine)) {
if (connectToEngine(mRequestedEngine)) {
mCurrentEngine = mRequestedEngine;
return SUCCESS;
} else if (!mUseFallback) {
mCurrentEngine = null;
dispatchOnInit(ERROR);
return ERROR;
}
} else if (!mUseFallback) {
Log.i(TAG, "Requested engine not installed: " + mRequestedEngine);
mCurrentEngine = null;
dispatchOnInit(ERROR);
return ERROR;
}
}
// Step 2: Try connecting to the user's default engine.
final String defaultEngine = getDefaultEngine();
if (defaultEngine != null && !defaultEngine.equals(mRequestedEngine)) {
if (connectToEngine(defaultEngine)) {
mCurrentEngine = defaultEngine;
return SUCCESS;
}
}
// Step 3: Try connecting to the highest ranked engine in the
// system.
final String highestRanked = mEnginesHelper.getHighestRankedEngineName();
if (highestRanked != null && !highestRanked.equals(mRequestedEngine) &&
!highestRanked.equals(defaultEngine)) {
if (connectToEngine(highestRanked)) {
mCurrentEngine = highestRanked;
return SUCCESS;
}
}
// NOTE: The API currently does not allow the caller to query whether
// they are actually connected to any engine. This might fail for various
// reasons like if the user disables all her TTS engines.
mCurrentEngine = null;
dispatchOnInit(ERROR);
return ERROR;
}
然后主要看connectToEngine(),去绑定服务。
private boolean connectToEngine(String engine) {
Connection connection = new Connection();
Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
intent.setPackage(engine);
boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
if (!bound) {
Log.e(TAG, "Failed to bind to " + engine);
return false;
} else {
Log.i(TAG, "Sucessfully bound to " + engine);
mConnectingServiceConnection = connection;
return true;
}
}
初始化完成,然后去开speak()方法。
@Deprecated
public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
return speak(text, queueMode, convertParamsHashMaptoBundle(params),
params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
}
进入speak(),这里提一下,注释建议我们设置一个话语进程监听器,就是setOnUtteranceProgressListener()方法。
public int speak(final CharSequence text,
final int queueMode,
final Bundle params,
final String utteranceId) {
return runAction(new Action<Integer>() {
@Override
public Integer run(ITextToSpeechService service) throws RemoteException {
Uri utteranceUri = mUtterances.get(text);
if (utteranceUri != null) {
return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
getParams(params), utteranceId);
} else {
return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
utteranceId);
}
}
}, ERROR, "speak");
}
进去看runAction()
private <R> R runAction(Action<R> action, R errorResult, String method) {
return runAction(action, errorResult, method, true, true);
}
再点进去看runAction(action, errorResult, method, true, true);
private <R> R runAction(Action<R> action, R errorResult, String method,
boolean reconnect, boolean onlyEstablishedConnection) {
synchronized (mStartLock) {
if (mServiceConnection == null) {
Log.w(TAG, method + " failed: not bound to TTS engine");
return errorResult;
}
return mServiceConnection.runAction(action, errorResult, method, reconnect,
onlyEstablishedConnection);
}
}
都是一些判断,再进去runAction(action, errorResult, method, reconnect,onlyEstablishedConnection);
public <R> R runAction(Action<R> action, R errorResult, String method,
boolean reconnect, boolean onlyEstablishedConnection) {
synchronized (mStartLock) {
try {
if (mService == null) {
Log.w(TAG, method + " failed: not connected to TTS engine");
return errorResult;
}
if (onlyEstablishedConnection && !isEstablished()) {
Log.w(TAG, method + " failed: TTS engine connection not fully set up");
return errorResult;
}
return action.run(mService);
} catch (RemoteException ex) {
Log.e(TAG, method + " failed", ex);
if (reconnect) {
disconnect();
initTts();
}
return errorResult;
}
}
}
可以看到最终回调 action.run();,紧接着执行ITextToSpeechService类的playAudio(),
@Override
public int playAudio(
IBinder caller,
Uri audioUri,
int queueMode,
Bundle params,
String utteranceId) {
if (!checkNonNull(caller, audioUri, params)) {
return TextToSpeech.ERROR;
}
SpeechItem item =
new AudioSpeechItem(
caller,
Binder.getCallingUid(),
Binder.getCallingPid(),
params,
utteranceId,
audioUri);
return mSynthHandler.enqueueSpeechItem(queueMode, item);
}
然后就进入将语音项添加到队列中的方法enqueueSpeechItem(queueMode, item);
public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) {
UtteranceProgressDispatcher utterenceProgress = null;
if (speechItem instanceof UtteranceProgressDispatcher) {
utterenceProgress = (UtteranceProgressDispatcher) speechItem;
}
if (!speechItem.isValid()) {
if (utterenceProgress != null) {
utterenceProgress.dispatchOnError(
TextToSpeech.ERROR_INVALID_REQUEST);
}
return TextToSpeech.ERROR;
}
if (queueMode == TextToSpeech.QUEUE_FLUSH) {
stopForApp(speechItem.getCallerIdentity());
} else if (queueMode == TextToSpeech.QUEUE_DESTROY) {
stopAll();
}
Runnable runnable = new Runnable() {
@Override
public void run() {
if (setCurrentSpeechItem(speechItem)) {
speechItem.play();
removeCurrentSpeechItem();
} else {
// The item is alreadly flushed. Stopping.
speechItem.stop();
}
}
};
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)) {
return TextToSpeech.SUCCESS;
} else {
Log.w(TAG, "SynthThread has quit");
if (utterenceProgress != null) {
utterenceProgress.dispatchOnError(TextToSpeech.ERROR_SERVICE);
}
return TextToSpeech.ERROR;
}
}
主要看play();
public void play() {
synchronized (this) {
if (mStarted) {
throw new IllegalStateException("play() called twice");
}
mStarted = true;
}
playImpl();
}
进入playImpl();
@Override
protected void playImpl() {
AbstractSynthesisCallback synthesisCallback;
mEventLogger.onRequestProcessingStart();
synchronized (this) {
// stop() might have been called before we enter this
// synchronized block.
if (isStopped()) {
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();
}
}
会执行到onSynthesizeText()方法,最终又回到系统提供的pico引擎中。
到这里,TTS就调用完毕。
网友评论