在上一篇中,我们介绍了使用MediaPlayer配合TextToSpeech实现英文的暂停与播放,实际使用中,中文的场景还是远远大于英文场景的,所以今天我们使用另一套方案实现中文TTS播报的暂停与恢复。这部分内容,目前在网络上是找不到的,里面的一些参数我暂时也不会给出来,如果有需要,可以评论私聊沟通一下。github地址
方案主要包括TextToSpeech,科大讯飞语音+即可,主要实现步骤:
- 安装科大讯飞语音+ APP,并进行设置,可以参考Android原生TTS的基本使用以及配合中文语音包实现中文TTS
- 对TextToSpeech进行封装处理
(1)初始化
(2)设置参数
(3)根据语速参数获取charStep值
(4)重写播报,暂停与恢复等接口功能
下面就来看一下封装的TTS管理类,最后给几点说明:
package aoto.com.ttstest;
import android.content.Context;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.util.Log;
import java.util.HashMap;
/**
* author:why
* created on: 2019/7/13 8:49
* description:
*/
public class ChinaTTSManager implements WhyTTS{
private static final String TAG = "ChinaTTSManagerWhy";
private static volatile WhyTTS whyTTS=null;
private TextToSpeech mSpeech;
private String residenceContent;
private HashMap<Integer,Long> sentenceStep=new HashMap<>();
private long startTime=0;
private long duration=0;
private long charStep=??;
private long markStep=??;
private ChinaTTSManager(Context context){
mSpeech=new TextToSpeech(context, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
Log.e(TAG, "onInit: success" );
//mSpeech.setSpeechRate(1.0f); 默认就是1.0f
}
});
}
/**
* DCL
* @param context
* @return
*/
public static WhyTTS getInstance(Context context){
if(whyTTS==null){
synchronized (ChinaTTSManager.class){
if(whyTTS==null){
whyTTS=new ChinaTTSManager(context);
}
}
}
return whyTTS;
}
@Override
public void speak(final String content) {
residenceContent=content;
//sentenceStep.clear();
//getSentenceStep();
startTime=System.currentTimeMillis();
mSpeech.speak(content,TextToSpeech.QUEUE_FLUSH,null);
}
@Override
public void pause(){
duration=System.currentTimeMillis()-startTime;
residenceContent=getResidenceByDuration(duration);
mSpeech.speak("",TextToSpeech.QUEUE_FLUSH,null);
}
@Override
public void resume(){
speak(residenceContent);
}
@Override
public void setSpeechRate(float newRate) {
if(mSpeech!=null){
mSpeech.setSpeechRate(newRate);
//TODO update charStep
charStep=charStep+(long)(newRate-1.0f)*charStep;
}
}
@Override
public void setSpeechPitch(float newPitch) {
if(mSpeech!=null){
mSpeech.setSpeechRate(newPitch);
}
}
private String getResidenceByDuration(long duration){
int tempIndex= (int) (duration/charStep);
if(duration > (charStep * residenceContent.length())){
return "";
}
residenceContent=residenceContent.substring(tempIndex-1);
return residenceContent;
// int index=findSentenceIndex(duration);
// if(index==-1){
// return "";
// }else {
// residenceContent=residenceContent.substring((int)((duration-sentenceStep.get(index-1))/charStep)+1);
// return residenceContent;
// }
}
/**
* 根据朗读时间计算当前读到句子索引
* @param duration
*/
private int findSentenceIndex(long duration) {
for(int i=0;i<sentenceStep.size()-1;i++){
if(duration<=sentenceStep.get(i+1)&&duration>=sentenceStep.get(i)){
return i+1;
}
}
if(duration>sentenceStep.get(sentenceStep.size()-1)){
return -1;
}
return -2;
}
/**
* get the mark index list
*/
private void getSentenceStep() {
String[] array=residenceContent.split(",");
if(array.length<=1){
return;
}else {
for(int i=0;i<array.length;i++){
long tempTime=0;
for(int j=0;j<=i;j++){
tempTime+=array[j].length()*charStep;
}
tempTime+=(i+1)*markStep;
sentenceStep.put(i,tempTime);
}
}
}
}
其中WhyTTS:
package aoto.com.ttstest;
/**
* author:why
* created on: 2019/7/13 10:56
* description: TTS interface
*/
public interface WhyTTS {
void speak(final String content);
void pause();
void resume();
void setSpeechRate(float newRate);
void setSpeechPitch(float newPitch);
}
下面给出几点说明如下:
思想:获取每一个汉字播报占用时长
(1)这个方案实现虽然简单且效果一般,但是在功能实现上是满足需求的
(2)这种方案控制效果一般,可能会多读一两个字
(3)可以根据符号断句进行优化,我这里没有放开(getSentenceStep()与findSentenceIndex()未使用),且这里使用的是针对单个符号“,”,所以对语料需要进行比较严格的编辑。精准控制,需要进行大量的测试,目前的效果基本上就可以满足一般的需求。
注:如果对你有帮助,欢迎扫码关注