TextToSpeech文字转语音、文字转音频文件并播放

TextToSpeech文字转语音、文字转音频文件并播放

前段时间遇到了语音读网页的需求,特地在网上找了一些资料。学习完毕后跟大家分享一下。这里是我从项目中抽取出来的代码,大家应该也基本能看清楚流程了。上代码

首先是文字转语音及文件的工具类

import android.content.Context;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import android.util.Log;

import java.io.File;
import java.util.Locale;

/**
 * Created by ly on 2018/8/20.
 */

public class TTSHelper extends UtteranceProgressListener {
    private TextToSpeech tts;
    private boolean isSupportCN = true;


    public TTSHelper(Context context) {
        tts = new TextToSpeech(context, status -> {
            if (status == TextToSpeech.SUCCESS) {
                int result = tts.setLanguage(Locale.CHINA);
                tts.setPitch(1.0f);// 设置音调,值越大声音越尖(女生),值越小则变成男声,1.0是常规
                tts.setSpeechRate(1.0f);//设置语速
                tts.setOnUtteranceProgressListener(TTSHelper.this);
                if (result == TextToSpeech.LANG_NOT_SUPPORTED || result == TextToSpeech.LANG_MISSING_DATA) {
                    isSupportCN = false;//该设备是否支持设置语音的标记
                }
            }
        });
    }

    /**
     *
     * @param content 要转换成文件的文字内容
     * @param path 音频文件地址 xxx.mp3
     * @return
     */
    public int textToFile(String content, File path) {
        int i = tts.synthesizeToFile(content,null,path, "pensoin-record");
        return i;
    }

    @Override
    public void onStart(String utteranceId) {
        Log.d("xulc", "onStart---utteranceId--->" + utteranceId);
    }

    @Override
    public void onDone(String utteranceId) {
        Log.d("xulc", "onDone---utteranceId--->" + utteranceId);
    }

    @Override
    public void onError(String utteranceId) {
        Log.d("xulc", "onError---utteranceId--->" + utteranceId);
    }


    public TextToSpeech getTts() {
        return tts;
    }

    public boolean isSupportCN() {
        return isSupportCN;
    }


    /**
     * 朗读 文本
     * @param content
     */
    public void start(String content) {
        getTts().speak(content, TextToSpeech.QUEUE_FLUSH, null, null);
    }

    public void stop() {
        getTts().stop();
    }

    public void destroy() {
        getTts().shutdown();
        tts = null;
    }

}

文字转音频文件的时候,遇到了一个问题,后来追踪了一下源码,发现文字内容不能超过4000个字。。
TextToSpeech文字转语音、文字转音频文件并播放_第1张图片
TextToSpeech文字转语音、文字转音频文件并播放_第2张图片
点击进入getMaxSpeechInputLength方法,发现最大长度为4000。
后来实行分片保存音频的方式。

这里提供方法

/**
     * 根据内容获取文件名称列表
     * @param content
     * @return
     */
    private List getFileNames(String content) {
        List names = new ArrayList<>();
        int length = content.length();//每一千个文字保存成一个文件
        if (length < 1000) {
            names.add(id + "_1");
            return names;
        } else {
            int fileCount;
            if (length % 1000 == 0) {
                fileCount = content.length() / 1000;
            } else {
                fileCount = content.length() / 1000 + 1;
            }
            for (int i = 1; i <= fileCount; i++) {
                names.add(id + "_" + i);//这里的id是资讯文章的id由后台返回
            }
            return names;
        }
    }

然后生成文件名

/**
     * 根据列表返回文件列表
     * @param names
     * @return
     */
    public static List getFiles(List names){
        String sd = Environment.getExternalStorageDirectory().getAbsolutePath(); // 取得SD卡
        //Config.BASE_FILE_DIR 及 INFORMATION_VOICE_FILE_PATH 随意定
        File file = new File(sd + Config.BASE_FILE_DIR + "/" + INFORMATION_VOICE_FILE_PATH + "/");
        if (!file.exists()) { // 如果文件夹不存在
           file.mkdirs();// 创建文件夹
        }
        List files = new ArrayList<>();
        for (int i = 0;i

获取文字切割列表

/**
     * 
     * @param content
     * @return
     */
    private List getSubContents(String content){
        int length = content.length();

        int count;
        if (length % 1000 == 0) {
            count = length / 1000 ;
        } else {
            count = length / 1000 + 1;
        }
        List contents = new ArrayList<>();
        int start = 0;
        String temp;
        for(int i = 0;i

这里说明简要说明如何调用及思维方式
我们可以在第一个音频播放以后,自己调用代码,将切割好的文字列表静默转成音频文件.
待第一个音频播放完毕以后,自动播放下一个已经保存好的音频文件。这样就不用考虑文件转换的耗时问题了。此处只说明方法,代码需要朋友们自己去写一下。

private File file;
    private PlayerUtil player;
    private String content;
    private void start(){
    //String content = 
        List fileNames = getFileNames(content);
        String currContent = getSubContents(content).get(0);
        file = Config.getFiles(fileNames).get(0);//预先加载第一个文件
        
        if (file.exists()) {//如果存在缓存文件,直接播放
            play(file);
//            webVoiceLayout.setVisibility(View.VISIBLE);
//            webVoiceText.setText(content);//跑马灯文字
//            webVoiceText.setSelected(true);//跑马灯文字许要设施的属性
        } else {
            int i = ttsHelper.textToFile(currContent, file);//如果没有缓存,则转成音频
            if (i == TextToSpeech.SUCCESS) {
//                showDialog("加载中..");
                playerHandler.postDelayed(() -> {//由于转音频是异步的,并且没有完成的监听,所以我们加一个延时,1000字2.5秒基本已经转完了
//                    webVoiceLayout.setVisibility(View.VISIBLE);
//                    webVoiceText.setText(content);
//                    webVoiceText.setSelected(true);
                    play(file);
                }, 2500);
            } else {
//                Logger.toast("音频文件保存失败..");//只要小于4000应该不会失败,目前我没遇到失败
                file.delete();
            }
        }
    }

    private void play(File file) {
        if (player == null) {
            player = new PlayerUtil();
//            initSeekBar();
        } else {
            player.reset();
        }
        try {
//            player.play(URL);
            player.play(file);
//            webVoiceSeek.setMax(player.getDuration());//播放进度条(由于超过4000字的音频分片了,进度不好控制)
//            webVoiceSeek.setProgress(0);
            startPlayer();
        } catch (IOException e) {//出现该异常,基本是MediaPlayer prepare出错,因为音频文件还在转换过程中
            handler.postDelayed(() -> {//所以加个延时,再重新加载音频文件
                play(file);
            }, 2000);
            e.printStackTrace();
        }
    }

    @SuppressLint("HandlerLeak")	
    private Handler playerHandler = new Handler() {//handler用于更新seekbar进度
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 0x01) {
//                webVoiceSeek.setProgress(player.getCurrentPosition());
                sendEmptyMessageDelayed(0x01, 1000);
            }
        }
    };


    public void startPlayer() {
        //hideDialog();
        player.startPlayer();
        playerHandler.sendEmptyMessage(0x01);
        webVoiceState.setImageResource(R.mipmap.pause);
    }

    public void pausePlayer() {
        player.pausePlayer();
        webVoiceState.setImageResource(R.mipmap.start);
    }

    public void resetPlayer() {
        player.stopPlayer();
        webVoiceState.setImageResource(R.mipmap.start);
    }

    public void destroyPlayer() {
        if (player != null) {
            player.destroyPlayer();
            player = null;
        }
        playerHandler.removeCallbacksAndMessages(null);
    }

下面给出播放器的代码

import android.media.MediaPlayer;

import java.io.File;
import java.io.IOException;

public class PlayerUtil {

    private MediaPlayer player;

    public PlayerUtil() {
    }

    public void play(File file) throws IOException {
        if (player == null) {
            player = new MediaPlayer();
        }
        player.setDataSource(file.getAbsolutePath());
        player.prepare();
        startPlayer();
    }

    public void play(String url) throws IOException {
        if (player == null) {
            player = new MediaPlayer();
        }
        player.setDataSource(url);
        player.prepare();
        startPlayer();
    }

    public int getDuration() {
        return player.getDuration();
    }

    public void startPlayer() {
        player.start();
    }

    public void pausePlayer() {
        player.pause();
    }

    public void stopPlayer() {
        player.stop();
    }

    public void reset(){
        player.reset();
    }

    public int getCurrentPosition(){
        return player.getCurrentPosition();
    }

    public void seekTo(int progress){
        player.seekTo(progress);
    }

    public boolean isPlaying(){
        return player.isPlaying();
    }

    public void destroyPlayer() {
        if (player != null) {
            player.stop();
            player.release();
            player = null;
        }
    }
}

TextToSpeech文字转语音、文字转音频文件并播放_第3张图片
分享就到这里,谢谢大家。欢迎互相学习

你可能感兴趣的:(android,TextToSpeech,文字转语音,安卓,android)