美文网首页
灵云TTS(语音合成)

灵云TTS(语音合成)

作者: liliLearn | 来源:发表于2017-09-30 11:17 被阅读0次

    项目中使用了TTS(语音合成功能)刚开始自己准备使用科大讯飞的TTS SDK 但是公司经过半天调研(省钱)决定使用灵云的SDK。但是灵云的文档和Demo不是很完善而且网上资料很少,避免下次挖坑自己封装了一个TtsManage。
    灵云的TTS分为在线模式和本地模式,在线的可以通过修改配置更改发音人,离线模式只能通过在项目中的发音人文件发音。

    一、引入SDK和so文件

    http://www.hcicloud.com/dev/appendix/evninstall

    这里写图片描述

    二、配置Manifest文件

    在工程AndroidManifest.xml文件中添加如下权限:

     <!—如果使用录音机API时,需要RECORD_AUDIO权限,否则不需要 -->
            <uses-permission android:name="android.permission.RECORD_AUDIO" />
            <!—通常需要设置一些sd卡路径(例如日志路径)为可写,因此需要能够写外部存储 -->
            <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
            <!—以下访问网络的权限均需要打开-->
            <uses-permission android:name="android.permission.INTERNET" />
            <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
            <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
            <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
            <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
            <!—以下访问权限可选-->
            <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    

    三、制作本地发音人文件

    下载地址

    这里写图片描述

    为了便于应用的发布与管理,灵云SDK也支持将本地资源文件随应用安装在内部存储上, 但此种存放方法需要开发者满足以下要求,灵云SDK才能正常访问这些资源文件:

    1. 对资源文件进行特殊命名。以本地能力(tts.local.xiaokun)所需要的TTS资源文件为例,以下示例了这种命名规则:

    libXiaoKun.voclib.so(在原文件名前加lib前缀与.so后缀)
    libLetter_XiaoKun.voclib.so(在原文件名前加lib前缀与.so后缀)

    1. 将重新命名过的本地资源文件拷贝到libs根目录下。

    2. 在能力初始化时,指定dataPath参数为 /data/data/appname/lib (appname为应用包的名称)

    3. 在能力初始化时,指定fileFlag参数为android_so(默认为none)

    所有需要的本地资源文件都需要按以上规则进行名字的改动,并拷贝到libs目录下。 通过这种方式,这些资源文件会被打包在APK中,并在安装时,被放在 /data/data/appname/lib 下。 通过指定fileFlag为android_so,灵云SDK就会按照特殊的命名方式来读取这些文件。


    这里写图片描述

    把这4个文件拷贝到Lib目标下,我们的环境就算搭建好了!下面开始写代码。
    1.配置信息

    /**
     * 灵云配置信息
     */
    public final class ConfigUtil {
    
        /**
         * 灵云APP_KEY
         */
        public static final String APP_KEY = "005d5493";
    
        /**
         * 开发者密钥
         */
        public static final String DEVELOPER_KEY = "36599a64ff4cb08ffa916544f38c9002";
    
        /**
         * 灵云云服务的接口地址
         */
        public static final String CLOUD_URL = "test.api.hcicloud.com:8888";
    
        /**
         * 需要运行的灵云能力
         */
        // 离线语音合成
        public static final String CAP_KEY_TTS_LOCAL = "tts.local.synth";
    
        // 云端语音合成
        public static final String CAP_KEY_TTS_CLOUD = "tts.cloud.wangjing";
    
    }
    

    2.初始化TTS

    import android.content.Context;
    import android.os.Environment;
    import android.util.Log;
    import android.widget.Toast;
    
    import com.sinovoice.hcicloudsdk.api.HciCloudSys;
    import com.sinovoice.hcicloudsdk.common.AuthExpireTime;
    import com.sinovoice.hcicloudsdk.common.HciErrorCode;
    import com.sinovoice.hcicloudsdk.common.InitParam;
    
    import java.io.File;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Locale;
    
    public class TtsUtil {
        private final String TAG = this.getClass().getSimpleName();
        private  InitParam initParam;
        private final boolean isSaveLog=false;
    
        public TtsUtil(Context context) {
            setInitParam(context);
            checkStatus(context);
        }
    
        public boolean checkStatus(Context context) {
            // 初始化
            int errCode = HciCloudSys.hciInit(initParam.getStringConfig(), context);
            if (errCode != HciErrorCode.HCI_ERR_NONE && errCode != HciErrorCode.HCI_ERR_SYS_ALREADY_INIT) {
                Toast.makeText(context, "hciInit error: " + HciCloudSys.hciGetErrorInfo(errCode), Toast.LENGTH_SHORT).show();
                return false;
            }
    
            // 获取授权/更新授权文件 :
            errCode = checkAuthAndUpdateAuth();
            if (errCode != HciErrorCode.HCI_ERR_NONE) {
                // 由于系统已经初始化成功,在结束前需要调用方法hciRelease()进行系统的反初始化
                Toast.makeText(context, "CheckAuthAndUpdateAuth error: " + HciCloudSys.hciGetErrorInfo(errCode), Toast.LENGTH_SHORT).show();
                HciCloudSys.hciRelease();
                return false;
            }
    
            if (isSaveLog) {
                saveLog(context.getPackageName(), initParam);
            }
            return true;
        }
    
        /**
         * 加载初始化信息
         *
         * @return 系统初始化参数
         */
        private void setInitParam(Context context) {
            String authDirPath = context.getFilesDir().getAbsolutePath();
            // 前置条件:无
            initParam= new InitParam();
            // 授权文件所在路径,此项必填
            initParam.addParam(InitParam.AuthParam.PARAM_KEY_AUTH_PATH, authDirPath);
            // 是否自动访问云授权,详见 获取授权/更新授权文件处注释
            initParam.addParam(InitParam.AuthParam.PARAM_KEY_AUTO_CLOUD_AUTH, "no");
            // 灵云云服务的接口地址,此项必填
            initParam.addParam(InitParam.AuthParam.PARAM_KEY_CLOUD_URL, ConfigUtil.CLOUD_URL);
            // 开发者Key,此项必填,由捷通华声提供
            initParam.addParam(InitParam.AuthParam.PARAM_KEY_DEVELOPER_KEY, ConfigUtil.DEVELOPER_KEY);
            // 应用Key,此项必填,由捷通华声提供
            initParam.addParam(InitParam.AuthParam.PARAM_KEY_APP_KEY, ConfigUtil.APP_KEY);
        }
    
        public void saveLog(String packageName, InitParam initparam) {
            // 配置日志参数
            String sdcardState = Environment.getExternalStorageState();
            if (Environment.MEDIA_MOUNTED.equals(sdcardState)) {
                String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();
                String logPath = sdPath + File.separator + "sinovoice" + File.separator + packageName + File.separator + "log" + File.separator;
                // 日志文件地址
                File fileDir = new File(logPath);
                if (!fileDir.exists()) {
                    fileDir.mkdirs();
                }
                // 日志的路径,可选,如果不传或者为空则不生成日志
                initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_PATH, logPath);
                // 日志数目,默认保留多少个日志文件,超过则覆盖最旧的日志
                initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_COUNT, "5");
                // 日志大小,默认一个日志文件写多大,单位为K
                initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_SIZE, "1024");
                // 日志等级,0=无,1=错误,2=警告,3=信息,4=细节,5=调试,SDK将输出小于等于logLevel的日志信息
                initparam.addParam(InitParam.LogParam.PARAM_KEY_LOG_LEVEL, "5");
            }
        }
    
        private int checkAuthAndUpdateAuth() {
            // 获取系统授权到期时间
            int initResult;
            AuthExpireTime objExpireTime = new AuthExpireTime();
            initResult = HciCloudSys.hciGetAuthExpireTime(objExpireTime);
            if (initResult == HciErrorCode.HCI_ERR_NONE) {
                // 显示授权日期,如用户不需要关注该值,此处代码可忽略
                Date date = new Date(objExpireTime.getExpireTime() * 1000);
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
                Log.i(TAG, "expire time: " + sdf.format(date));
                if (objExpireTime.getExpireTime() * 1000 > System.currentTimeMillis()) {
                    // 已经成功获取了授权,并且距离授权到期有充足的时间(>7天)
                    Log.i(TAG, "checkAuth success");
                    return initResult;
                }
            }
            // 获取过期时间失败或者已经过期
            initResult = HciCloudSys.hciCheckAuth();
            if (initResult == HciErrorCode.HCI_ERR_NONE) {
                Log.i(TAG, "checkAuth success");
                return initResult;
            } else {
                Log.e(TAG, "checkAuth failed: " + initResult);
                return initResult;
            }
        }
    
        /**
         * 释放
         */
        public void hciRelease(){
            HciCloudSys.hciRelease();
        }
    }
    

    3.初始化语音播放

    import android.app.Activity;
    import android.util.Log;
    import android.widget.Toast;
    
    import com.sinovoice.hcicloudsdk.android.tts.player.TTSPlayer;
    import com.sinovoice.hcicloudsdk.common.asr.AsrInitParam;
    import com.sinovoice.hcicloudsdk.common.hwr.HwrInitParam;
    import com.sinovoice.hcicloudsdk.common.tts.TtsConfig;
    import com.sinovoice.hcicloudsdk.common.tts.TtsInitParam;
    import com.sinovoice.hcicloudsdk.player.TTSCommonPlayer;
    import com.sinovoice.hcicloudsdk.player.TTSPlayerListener;
    
    /**
     * Created by kqw on 2016/8/12.
     * 初始化语音合成能力
     */
    public class TtsPlayUtil {
    
        private static final String TAG = "HciUtil";
        private Activity mActivity;
        private TTSPlayer mTtsPlayer;
    
        public TtsPlayUtil(Activity activity) {
            mActivity = activity;
        }
    
        /**
         * 初始化播放器
         */
        public boolean initPlayer(TTSPlayerListener ttsPlayerListener) {
            // 构造Tts初始化的帮助类的实例
            TtsInitParam ttsInitParam = new TtsInitParam();
            // 获取App应用中的lib的路径
            String dataPath = mActivity.getBaseContext().getFilesDir().getAbsolutePath().replace("files", "lib");
            Log.e("path",dataPath);
            ttsInitParam.addParam(TtsInitParam.PARAM_KEY_DATA_PATH, dataPath);
            // 此处演示初始化的能力为tts.cloud.xiaokun, 用户可以根据自己可用的能力进行设置, 另外,此处可以传入多个能力值,并用;隔开
            ttsInitParam.addParam(AsrInitParam.PARAM_KEY_INIT_CAP_KEYS, ConfigUtil.CAP_KEY_TTS_LOCAL);
            // 如果使用本地能力,需要设置本地音库文件路径
    
            // 使用lib下的资源文件,需要添加android_so的标记
            ttsInitParam.addParam(HwrInitParam.PARAM_KEY_FILE_FLAG, HwrInitParam.VALUE_OF_PARAM_FILE_FLAG_ANDROID_SO);
    
            mTtsPlayer = new TTSPlayer();
            // 配置TTS初始化参数
            mTtsPlayer.init(ttsInitParam.getStringConfig(), ttsPlayerListener);
    
            return mTtsPlayer.getPlayerState() == TTSPlayer.PLAYER_STATE_IDLE;
        }
    
    
        // 云端合成,不启用编码传输(默认encode=none)
        public void synth(String text) {
            // 配置播放器的属性。包括:音频格式,音库文件,语音风格,语速等等。详情见文档。
            TtsConfig ttsConfig = new TtsConfig();
            // 音频格式
            ttsConfig.addParam(TtsConfig.BasicConfig.PARAM_KEY_AUDIO_FORMAT, "pcm16k16bit");
    
            // 指定语音合成的能力(云端合成,发言人是XiaoKun)
            ttsConfig.addParam(TtsConfig.SessionConfig.PARAM_KEY_CAP_KEY, ConfigUtil.CAP_KEY_TTS_LOCAL);
            //
            ttsConfig.addParam(TtsConfig.BasicConfig.PARAM_KEY_SPEED, "5");
            // property为私有云能力必选参数,公有云传此参数无效
    //        ttsConfig.addParam("property", "cn_xiaokun_common");
    
            if (mTtsPlayer.getPlayerState() == TTSCommonPlayer.PLAYER_STATE_PLAYING || mTtsPlayer.getPlayerState() == TTSCommonPlayer.PLAYER_STATE_PAUSE) {
                mTtsPlayer.stop();
            }
    
            if (mTtsPlayer.getPlayerState() == TTSCommonPlayer.PLAYER_STATE_IDLE) {
                mTtsPlayer.play(text, ttsConfig.getStringConfig());
            } else {
                Toast.makeText(mActivity, "播放器内部状态错误", Toast.LENGTH_SHORT).show();
            }
        }
    
        /**
         * 释放
         */
        public void release() {
            if (null != mTtsPlayer) {
                mTtsPlayer.release();
            }
        }
    
    }
    

    5.最后一步在UI进行交换

    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.text.TextUtils;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Toast;
    
    import com.sinovoice.hcicloudsdk.player.TTSCommonPlayer;
    import com.sinovoice.hcicloudsdk.player.TTSPlayerListener;
    
    import net.yeah.liliLearn.utils.TtsPlayUtil;
    import net.yeah.liliLearn.utils.TtsUtil;
    
    public class MainActivity extends AppCompatActivity implements TTSPlayerListener {
        private TtsPlayUtil mTtsPlayUtil;
        private TtsUtil mInitTts;
        private boolean isInitPlayer;
        private EditText mInputMsgEdit;
        private Button mPlayerButton;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            initView();
            initTts();
        }
        private void initView(){
            mInputMsgEdit= (EditText) findViewById(R.id.input_msg_edit);
            mPlayerButton= (Button) findViewById(R.id.player_btn);
            mPlayerButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    synth(mInputMsgEdit.getText().toString());
                }
            });
        }
        private void initTts(){
            // 灵云语音工具类
            mInitTts = new TtsUtil(this);
            // 语音合成能力工具类
            mTtsPlayUtil = new TtsPlayUtil(this);
            // 初始化语音合成
            isInitPlayer = mTtsPlayUtil.initPlayer(this);
        }
        @Override
        public void onPlayerEventStateChange(TTSCommonPlayer.PlayerEvent playerEvent) {
    
        }
    
        @Override
        public void onPlayerEventProgressChange(TTSCommonPlayer.PlayerEvent playerEvent, int i, int i1) {
    
        }
    
        @Override
        public void onPlayerEventPlayerError(TTSCommonPlayer.PlayerEvent playerEvent, int i) {
    
        }
    
        public void synth(String msg) {
            if (!isInitPlayer) {
                Toast.makeText(this, "语音播报初始化失败", Toast.LENGTH_SHORT).show();
                return;
            }
            if (TextUtils.isEmpty(msg)) {
                Toast.makeText(this, "语音播报合成内容为空", Toast.LENGTH_SHORT).show();
                return;
            }
            mTtsPlayUtil.synth(msg);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mTtsPlayUtil != null) {
                mTtsPlayUtil.release();
            }
            if (null != mInitTts) {
                mInitTts.hciRelease();
            }
        }
    }
    

    打完收工!

    Demo地址:https://github.com/liliLearn/TtsManage-master

    相关文章

      网友评论

          本文标题:灵云TTS(语音合成)

          本文链接:https://www.haomeiwen.com/subject/grugextx.html