天天看点

Android TTS的暂停与恢复功能(中文实现)

        在上一篇中,我们介绍了使用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()未使用),且这里使用的是针对单个符号“,”,所以对语料需要进行比较严格的编辑。精准控制,需要进行大量的测试,目前的效果基本上就可以满足一般的需求。

注:如果对你有帮助,欢迎扫码关注

Android TTS的暂停与恢复功能(中文实现)