讯飞离线语音合成接入

讯飞离线语音合成接入:

文字转语音的方法

1.Google TextToSpeech + 中文语音引擎

Google提供了原生的方法TextToSpeech,但是不支持中文,sad…

不过可以用第三方的语音引擎,eg,讯飞,百度…

详情参考:

Android 文字转语音(中文) TextToSpeech+科大讯飞语音引擎3.0

Android文字转语音引擎(TTS)简单比较及下载

个人项目可以尝试用,如果上线项目总不能让用户去下载语音引擎吧

2.第三方语音服务商(讯飞,百度)

接入的是讯飞离线语音SDK

  • 注册讯飞开放平台账号

​ 创建新应用-SDK下载-选择你需要的功能/服务

  • SDK下载/导入
    讯飞离线语音合成接入_第1张图片
    有readme文件,这个必须要读,

sample文件夹里,就是Demo代码,主要包含以下功能

讯飞离线语音合成接入_第2张图片

因为他的demo缺少了很多配置文件,并不能跑起来,所以不仅要配置app目录,project的build.gradle等,还要按照readme提示,添加文件

从讯飞下载的Demo中,是自带APPID的,没有的话需要自己添加

讯飞初始化的地方放在了应用的Application的onCreate中

StringBuffer param = new StringBuffer();
param.append("appid="+getString(R.string.app_id));
param.append(",");
// 设置使用v5+
param.append(SpeechConstant.ENGINE_MODE+"="+SpeechConstant.MODE_MSC);
SpeechUtility.createUtility(SpeechApp.this, param.toString());

我们需要的功能是离线语音合成,点击体验语音合成进入页面
讯飞离线语音合成接入_第3张图片

就可以操作了

封装了一个类处理SpeechSynthesizer

public class TtsManager {
    private SpeechSynthesizer mTts;
    private static final String TAG = TtsManager.class.getSimpleName();
    // 默认云端发音人
    public static String voicerCloud="xiaoyan";
    // 默认本地发音人
    public static String voicerLocal="xiaoyan";
    private Context mContext;
    public TtsManager(Context context) {
        mContext = context;
        Log.d(TAG,mTtsInitListener+"");
        mTts = SpeechSynthesizer.createSynthesizer(mContext, mTtsInitListener);
        Log.d(TAG,"SUCCESS");
    }


    // 初始化成功,之后可以调用startSpeaking方法
    // 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成,
    // 正确的做法是将onCreate中的startSpeaking调用移至这里
    InitListener mTtsInitListener = new InitListener() {
        @Override
        public void onInit(int code) {
            Log.d(TAG, "InitListener init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                if (onTtsInitListener != null) {
                    onTtsInitListener.initError();
                }
                Log.d(TAG, "初始化失败,错误码:" + code + ",请点击网址https://www.xfyun.cn/document/error-code查询解决方案");
            } else {
                // 初始化成功,之后可以调用startSpeaking方法
                // 注:有的开发者在onCreate方法中创建完合成对象之后马上就调用startSpeaking进行合成,
                // 正确的做法是将onCreate中的startSpeaking调用移至这里
                if (onTtsInitListener != null) {
                    Log.d(TAG, "开始播放");
                    onTtsInitListener.initSpeak();
                }
                Log.d(TAG, "初始化成功,开始播放");
            }
        }
    };


   private OnTtsInitListener onTtsInitListener;
    public void setOnTtsInitListener(OnTtsInitListener ttsInitListener){
          this.onTtsInitListener = ttsInitListener;
    }
    public interface OnTtsInitListener{

       void initError();
       void initSpeak();

    }
    private SynthesizerListener mTtsListener = new SynthesizerListener() {
        @Override
        public void onSpeakBegin() { //开始播放

        }



        @Override
        public void onSpeakPaused() { //暂停播放

        }

        @Override
        public void onSpeakResumed() { //继续播放

        }
        @Override
        public void onBufferProgress(int i, int i1, int i2, String s) {
          //合成进度
        }
        @Override
        public void onSpeakProgress(int i, int i1, int i2) {
          //播放进度
        }

        @Override
        public void onCompleted(SpeechError speechError) {
         //播放完成
        }

        @Override
        public void onEvent(int i, int i1, int i2, Bundle bundle) {
        // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
            // 若使用本地能力,会话id为null
            // if (SpeechEvent.EVENT_SESSION_ID == eventType) {
            //    String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
            //    Log.d(TAG, "session id =" + sid);
            // }

            //实时音频流输出参考
         /*if (SpeechEvent.EVENT_TTS_BUFFER == eventType) {
            byte[] buf = obj.getByteArray(SpeechEvent.KEY_EVENT_TTS_BUFFER);
            Log.e("MscSpeechLog", "buf is =" + buf);
         }*/
        }
    };

    /**
     * 默认本地
     * @param text
     * @return
     */
    public int startLocalSpeaking(String text){
        setParam(SpeechConstant.TYPE_LOCAL,voicerLocal);
        return  mTts.startSpeaking(text,mTtsListener);
    }

    //开始合成/播放
    public int startSpeaking(String text,String type,String voicer){
        setParam(type,voicer);
      return  mTts.startSpeaking(text,mTtsListener);
    }
   //停止播放
   public void stopSpeaking(){
       if( null != mTts ) {
           mTts.stopSpeaking();
       }
    }
    //暂停播放
    public void pauseSpeaking(){
        mTts.pauseSpeaking();
    }

    public void destroySpeaking(){
        if( null != mTts ){
            mTts.stopSpeaking();
            // 退出时释放连接
            mTts.destroy();
        }
    }

    //继续播放
    private void resumeSpeaking(){
        mTts.resumeSpeaking();
    }



    private HashMap getTtsParam(){
        HashMap hashMap = new HashMap();
        hashMap.put(SpeechConstant.SPEED,"50");////设置合成语速
        hashMap.put(SpeechConstant.PITCH,"50");//   //设置合成音调
        hashMap.put(SpeechConstant.VOLUME,"50");//   //设置合成音量
        hashMap.put(SpeechConstant.STREAM_TYPE,"3");//   //  //设置播放器音频流类型
        hashMap.put(SpeechConstant.KEY_REQUEST_FOCUS,"50");//   设置播放合成音频打断音乐播放,默认为true
        hashMap.put(SpeechConstant.AUDIO_FORMAT,"wav");//  设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        hashMap.put(SpeechConstant.TTS_AUDIO_PATH,Environment.getExternalStorageDirectory()+"/msc/tts.wav");
       return hashMap;
       /* //设置合成音调
        mTts.setParameter(SpeechConstant.PITCH, TtsSpUtils.getInstance().getString("pitch_preference", "50"));
        //设置合成音量
        mTts.setParameter(SpeechConstant.VOLUME, TtsSpUtils.getInstance().getString("volume_preference", "50"));
        //设置播放器音频流类型
        mTts.setParameter(SpeechConstant.STREAM_TYPE, TtsSpUtils.getInstance().getString("stream_preference", "3"));
        // 设置播放合成音频打断音乐播放,默认为true
        mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/tts.wav");*/

    }
    private void setTtsParam(String key,String value){
        HashMap hashMap = getTtsParam();
        if (key!= null) {
            hashMap.put(key, value);
        }
        for (String mkey:hashMap.keySet()) {
            mTts.setParameter(mkey,hashMap.get(mkey));
        }
    }
    private void setLocalParam(){
        // 清空参数
        mTts.setParameter(SpeechConstant.PARAMS, null);
        //设置使用本地引擎
        mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
        //设置发音人资源路径
        mTts.setParameter(ResourceUtil.TTS_RES_PATH,getResourcePath());
        //设置发音人
        mTts.setParameter(SpeechConstant.VOICE_NAME,voicerLocal);
    //mTts.setParameter(SpeechConstant.TTS_DATA_NOTIFY,"1");//支持实时音频流抛出,仅在synthesizeToUri条件下支持
    //设置合成语速
        mTts.setParameter(SpeechConstant.SPEED, TtsSpUtils.getInstance().getString("speed_preference", "50"));


    }

    private void setParam(String mEngineType,String voicer){
        // 清空参数
        mTts.setParameter(SpeechConstant.PARAMS, null);
        //设置合成
        if(mEngineType.equals(SpeechConstant.TYPE_CLOUD))
        {
            //设置使用云端引擎
            mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
            //设置发音人
            mTts.setParameter(SpeechConstant.VOICE_NAME,   voicer == null?voicerCloud:voicer);
        }else {
            //设置使用本地引擎
            mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
            //设置发音人资源路径
            mTts.setParameter(ResourceUtil.TTS_RES_PATH,getResourcePath());
            //设置发音人
            mTts.setParameter(SpeechConstant.VOICE_NAME,voicer == null?voicerLocal:voicer);
        }
        //mTts.setParameter(SpeechConstant.TTS_DATA_NOTIFY,"1");//支持实时音频流抛出,仅在synthesizeToUri条件下支持
        //设置合成语速
        mTts.setParameter(SpeechConstant.SPEED, TtsSpUtils.getInstance().getString("speed_preference", "50"));
        //设置合成音调
        mTts.setParameter(SpeechConstant.PITCH, TtsSpUtils.getInstance().getString("pitch_preference", "50"));
        //设置合成音量
        mTts.setParameter(SpeechConstant.VOLUME, TtsSpUtils.getInstance().getString("volume_preference", "50"));
        //设置播放器音频流类型
        mTts.setParameter(SpeechConstant.STREAM_TYPE, TtsSpUtils.getInstance().getString("stream_preference", "3"));
        // 设置播放合成音频打断音乐播放,默认为true
        mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory()+"/msc/tts.wav");


    }
    //获取发音人资源路径
    private String getResourcePath(){
        StringBuffer tempBuffer = new StringBuffer();
        //合成通用资源
        tempBuffer.append(ResourceUtil.generateResourcePath(mContext, ResourceUtil.RESOURCE_TYPE.assets, "tts/common.jet"));
        tempBuffer.append(";");
        //发音人资源
        tempBuffer.append(ResourceUtil.generateResourcePath(mContext, ResourceUtil.RESOURCE_TYPE.assets, "tts/"+voicerLocal+".jet"));
        return tempBuffer.toString();
    }




}

还有一个SharedPreferences工具类:

public class TtsSpUtils {

    private static final String NAME = "Tts";
    private static TtsSpUtils instance = null;
    private SharedPreferences sp;

    public static TtsSpUtils getInstance() {
        synchronized (TtsSpUtils.class) {
            if (null == instance) {
                instance = new TtsSpUtils();
            }
        }
        return instance;
    }

    private TtsSpUtils() {

    }
    public void init(Context context){
        init(context,NAME);
    }
    public void init(Context context,String spName){
        sp = context.getApplicationContext().getSharedPreferences(spName,Context.MODE_PRIVATE);
    }
    /**
     * SP 中写入 String
     *
     * @param key   键
     * @param value 值
     */
    public void put(@NonNull final String key, @NonNull final String value) {
        put(key, value, false);
    }

    /**
     * SP 中写入 String
     *
     * @param key      键
     * @param value    值
     * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}
* {@code false}: {@link SharedPreferences.Editor#apply()} */
public void put(@NonNull final String key, @NonNull final String value, final boolean isCommit) { if (isCommit) { sp.edit().putString(key, value).commit(); } else { sp.edit().putString(key, value).apply(); } } /** * SP 中读取 String * * @param key 键 * @return 存在返回对应值,不存在返回默认值{@code ""} */ public String getString(@NonNull final String key) { return getString(key, ""); } /** * SP 中读取 String * * @param key 键 * @param defaultValue 默认值 * @return 存在返回对应值,不存在返回默认值{@code defaultValue} */ public String getString(@NonNull final String key, @NonNull final String defaultValue) { return sp.getString(key, defaultValue); } /** * SP 中写入 int * * @param key 键 * @param value 值 */ public void put(@NonNull final String key, final int value) { put(key, value, false); } /** * SP 中写入 int * * @param key 键 * @param value 值 * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}
* {@code false}: {@link SharedPreferences.Editor#apply()} */
public void put(@NonNull final String key, final int value, final boolean isCommit) { if (isCommit) { sp.edit().putInt(key, value).commit(); } else { sp.edit().putInt(key, value).apply(); } } /** * SP 中读取 int * * @param key 键 * @return 存在返回对应值,不存在返回默认值-1 */ public int getInt(@NonNull final String key) { return getInt(key, -1); } /** * SP 中读取 int * * @param key 键 * @param defaultValue 默认值 * @return 存在返回对应值,不存在返回默认值{@code defaultValue} */ public int getInt(@NonNull final String key, final int defaultValue) { return sp.getInt(key, defaultValue); } /** * SP 中写入 long * * @param key 键 * @param value 值 */ public void put(@NonNull final String key, final long value) { put(key, value, false); } /** * SP 中写入 long * * @param key 键 * @param value 值 * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}
* {@code false}: {@link SharedPreferences.Editor#apply()} */
public void put(@NonNull final String key, final long value, final boolean isCommit) { if (isCommit) { sp.edit().putLong(key, value).commit(); } else { sp.edit().putLong(key, value).apply(); } } /** * SP 中读取 long * * @param key 键 * @return 存在返回对应值,不存在返回默认值-1 */ public long getLong(@NonNull final String key) { return getLong(key, -1L); } /** * SP 中读取 long * * @param key 键 * @param defaultValue 默认值 * @return 存在返回对应值,不存在返回默认值{@code defaultValue} */ public long getLong(@NonNull final String key, final long defaultValue) { return sp.getLong(key, defaultValue); } /** * SP 中写入 float * * @param key 键 * @param value 值 */ public void put(@NonNull final String key, final float value) { put(key, value, false); } /** * SP 中写入 float * * @param key 键 * @param value 值 * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}
* {@code false}: {@link SharedPreferences.Editor#apply()} */
public void put(@NonNull final String key, final float value, final boolean isCommit) { if (isCommit) { sp.edit().putFloat(key, value).commit(); } else { sp.edit().putFloat(key, value).apply(); } } /** * SP 中读取 float * * @param key 键 * @return 存在返回对应值,不存在返回默认值-1 */ public float getFloat(@NonNull final String key) { return getFloat(key, -1f); } /** * SP 中读取 float * * @param key 键 * @param defaultValue 默认值 * @return 存在返回对应值,不存在返回默认值{@code defaultValue} */ public float getFloat(@NonNull final String key, final float defaultValue) { return sp.getFloat(key, defaultValue); } /** * SP 中写入 boolean * * @param key 键 * @param value 值 */ public void put(@NonNull final String key, final boolean value) { put(key, value, false); } /** * SP 中写入 boolean * * @param key 键 * @param value 值 * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}
* {@code false}: {@link SharedPreferences.Editor#apply()} */
public void put(@NonNull final String key, final boolean value, final boolean isCommit) { if (isCommit) { sp.edit().putBoolean(key, value).commit(); } else { sp.edit().putBoolean(key, value).apply(); } } /** * SP 中读取 boolean * * @param key 键 * @return 存在返回对应值,不存在返回默认值{@code false} */ public boolean getBoolean(@NonNull final String key) { return getBoolean(key, false); } /** * SP 中读取 boolean * * @param key 键 * @param defaultValue 默认值 * @return 存在返回对应值,不存在返回默认值{@code defaultValue} */ public boolean getBoolean(@NonNull final String key, final boolean defaultValue) { return sp.getBoolean(key, defaultValue); } /** * SP 中写入 String 集合 * * @param key 键 * @param values 值 */ public void put(@NonNull final String key, @NonNull final Set<String> values) { put(key, values, false); } /** * SP 中写入 String 集合 * * @param key 键 * @param values 值 * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}
* {@code false}: {@link SharedPreferences.Editor#apply()} */
public void put(@NonNull final String key, @NonNull final Set<String> values, final boolean isCommit) { if (isCommit) { sp.edit().putStringSet(key, values).commit(); } else { sp.edit().putStringSet(key, values).apply(); } } /** * SP 中读取 StringSet * * @param key 键 * @return 存在返回对应值,不存在返回默认值{@code Collections.emptySet()} */ public Set<String> getStringSet(@NonNull final String key) { return getStringSet(key, Collections.<String>emptySet()); } /** * SP 中读取 StringSet * * @param key 键 * @param defaultValue 默认值 * @return 存在返回对应值,不存在返回默认值{@code defaultValue} */ public Set<String> getStringSet(@NonNull final String key, @NonNull final Set<String> defaultValue) { return sp.getStringSet(key, defaultValue); } /** * SP 中获取所有键值对 * * @return Map 对象 */ public Map<String, ?> getAll() { return sp.getAll(); } /** * SP 中是否存在该 key * * @param key 键 * @return {@code true}: 存在
{@code false}: 不存在 */
public boolean contains(@NonNull final String key) { return sp.contains(key); } /** * SP 中移除该 key * * @param key 键 */ public void remove(@NonNull final String key) { remove(key, false); } /** * SP 中移除该 key * * @param key 键 * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}
* {@code false}: {@link SharedPreferences.Editor#apply()} */
public void remove(@NonNull final String key, final boolean isCommit) { if (isCommit) { sp.edit().remove(key).commit(); } else { sp.edit().remove(key).apply(); } } /** * SP 中清除所有数据 */ public void clear() { clear(false); } /** * SP 中清除所有数据 * * @param isCommit {@code true}: {@link SharedPreferences.Editor#commit()}
* {@code false}: {@link SharedPreferences.Editor#apply()} */
public void clear(final boolean isCommit) { if (isCommit) { sp.edit().clear().commit(); } else { sp.edit().clear().apply(); } } private static boolean isSpace(final String s) { if (s == null) return true; for (int i = 0, len = s.length(); i < len; ++i) { if (!Character.isWhitespace(s.charAt(i))) { return false; } } return true; } }

使用方式:

​ Application中的onCreate()中

  TtsSpUtils.getInstance().init(this);

在需要用的的Activity或者BaseActivity中

private TtsManager ttsManager;
/**
 * 开始语音播放.必须调该方法
 */
public  void initSpeech(String speakText){
  initSpeech(speakText,true);
}
public void initSpeech(String speakText,boolean isSpeak){
    if (!isSpeak) return;
    ttsManager = new TtsManager(this);
    ttsManager.setOnTtsInitListener(new TtsManager.OnTtsInitListener() {
        @Override
        public void initError() {
        }
        @Override
        public void initSpeak() {
            ttsManager.startLocalSpeaking(speakText);
        }
    });

}

Note: APPID 和 资源文件必须匹配,否则Log会提示错误

DEMO/Lib下载地址

1.使用下载的Demo,需要替换libs和assets的所有资源文件以及appid,考虑layout中的文件是否替换

2.如果依赖module,和上面一样需要替换资源文件,必须把libs中的libmsc.so文件放入 主项目的src/main/jniLibs/armeabi-v7a中

如果放在libs中,你必须sourceSet指明libs文件夹

把下面的maven地址放在Project的build.gradle里

 allprojects {
        repositories {
		
			// Msc.jar线上maven地址
		    maven{
                url 'http://libmsc.xfyun.cn/repository/maven-releases/'
            }
            jcenter()
            mavenCentral()
        }
    }

别忘了Application中的讯飞初始化

题外:目前讯飞1137版本在rk3399平台上初始化不成功,卡在初始方法上
参考:
科大讯飞在线语音合成
科大讯飞语音合成实例

你可能感兴趣的:(Android)