1. TTS 能力简介
语音合成技术(Text To Speech),简称 TTS,是一种把文字智能地转化为自然语音流的技术。通过采用神经网络算法,TTS 文本输出的语音音律流畅,使得听者在听取信息时感觉自然,毫无机器语音输出的冷漠与生涩感。TTS 通常应用在电子图书、汽车电子、智能仪器等领域。
灵云 TTS SDK 具备支持多语种、多音色、多接口、多支持等产品优势。在新的女娲合成引擎的作用下,更是有了带情感的合成能力。灵云 TTS SDK 已经成功应用在百度翻译、导航犬(navidog)、天行听书、快的打车等产品上。
本文档旨在讲解如何快速地集成灵云 TTS 功能到开发者应用中。关于各服务接口更详细的说明,请参考灵云SDK开发手册。在集成过程中如有疑问,可登录灵云开发者论坛,查找答案或与其他开发者交流。在阅读本文档之前,用户应该具备以下知识:
- 音频文件(pcm文件)的相关知识,如采样率等。
- 语音合成标记语言(SSML)的格式
- 应用开发的基本知识
1.1 概念解释
名称 | 说明 |
---|---|
Session | Session用来标记一个能力运行过程的上下文。一个应用最多可同时创建的Session数会受到授权的限制 |
云端合成 | 由云端服务器提供合成结果的方式。使用云端能力需要加上hci_tts_cloud_synth模块 |
本地合成 | 通过本地运算来得到合成结果的方式。使用本地能力需要加上hci_tts_local_synth模块和音库文件。目前提供v8,v9两种本地合成方式,v8是传统音色,v9是带情感的音色。 |
1.2 能力定义(capkey)
- 公有云云端中文能力
capkey | 音色 |
---|---|
tts.cloud.wangjing | 女声 |
tts.cloud.wangjing.v9 | 带情感的女声 |
tts.cloud.haobo | 男声 |
tts.cloud.xixi | 女童声 |
tts.cloud.xiaokun | 女声 |
- 私有云云端中文能力
capkey | 音色 |
---|---|
tts.cloud.synth | 私有云能力平台专用能力,配合property参数指定音色 |
- 本地 TTS 能力
capkey | 说明 | 所需本地资源文件 |
---|---|---|
tts.local.synth | 本地tts | CNPackage.dat,中文音库文件,必选 ENPackage.dat,英文音库文件,可选 DMPackage.dat,用户个性定制库,包括用户词表和用户录音 |
tts.local.synth.v9 | 本地带情感tts | CNPackage.dat,中文音库文件,必选 ENPackage.dat,英文音库文件,可选 DMPackage.dat,用户个性定制库,包括用户词表和用户录音 |
在TTS能力中,本地资源文件就是音库文件,可参考3.2.2 本地能力语音合成。
- 外文 TTS 能力 常用外文能力如下:
能力key | 音色 | 语种 |
---|---|---|
tts.cloud.serena | 女声 | 英语 |
tts.cloud.daniel | 男声 | 英语 |
tts.cloud.thomas | 男声 | 法语 |
tts.cloud.sebastien | 男声 | 法语 |
tts.cloud.kyoko | 女声 | 日语 |
tts.cloud.narae | 女声 | 韩语 |
其他请参考灵云SDK开发手册
1.3 使用流程
2. TTS 能力使用说明
2.1 准备工作
下载语音合成SDK并解压缩。
如果需要使用本地语音合成,请下载相应资源包并解压缩。
2.2 使用 Android 版 SDK
Android 版的 SDK 提供如下文件:
文件或目录 | 说明 |
---|---|
armeabi、armeabi-v7a、arm64-v8a | 存放灵云能力所需so文件的目录,依照不同的处理器架构分类 |
hcicloud-8.1.jar | 提供灵云能力接口的jar包 |
assets/data/ | 该目录下提供两种音色资源WangJing XiXi |
使用时将这些文件作为依赖添加到工程的目录下
2.3 库文件介绍
必选模块
- libcurl.so
- libhci_sys.so
- libhci_sys_jni.so
- libhci_tts.so
- libhci_tts_jni.so
云端合成
- libhci_tts_cloud_synth.so
- libjtspeex.so
- libjtopus.so
本地合成
- libhci_tts_local_synth.so
- libiHear.so
本地合成v9 带情感
- libhci_tts_local_synth_iHear9.so
- libiHear.so
目前提供的带情感库与普通库无法同时使用。
特别注意
Module
的build.gradle
中,必须配置packagingOptions.doNotStrip
参数,以保留灵云SDK中的签名信息,该信息在SDK内部使用,用来保护灵云SDK的知识版权。 若此参数未配置,或配置不正确,在进行本地能力调用时,将会返回本地引擎初始化失败的信息。
build.gradle示例
apply plugin: 'com.android.application' android { compileSdkVersion 17 buildToolsVersion "27.0.3" defaultConfig { applicationId "com.sinovoice.example" minSdkVersion 7 targetSdkVersion 17 packagingOptions { doNotStrip "*/armeabi/*.so" doNotStrip "*/armeabi-v7a/*.so" doNotStrip "*/arm64-v8a/*.so" // add other if needed } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' } } } dependencies { compile files('libs/hcicloud-8.1.jar') compile files('libs/hcicloud_recorder-8.1.jar') }
2.4 添加用户权限
在工程 AndroidManifest.xml 文件中添加如下权限。
<!—通常需要设置一些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" />
<!--获取当前wifi状态 -->
<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"/>
3. TTS 能力集成说明
3.1 灵云授权认证
3.1.1 初始化灵云 SDK 通用模块
在调用TTS能力之前,需要初始化灵云SDK的通用模块。详见灵云SDK开发手册
Android示例代码
// 创建初始化参数辅助类 InitParam initparam = new InitParam(); // 授权文件所在路径,此项必填 String authDirPath = context.getFilesDir() .getAbsolutePath();; initparam.addParam (InitParam.AuthParam.PARAM_KEY_AUTH_PATH, authDirPath) ; // 灵云云服务的接口地址,此项必填 initparam.addParam (InitParam.AuthParam.PARAM_KEY_CLOUD_URL, "http://api.hcicloud.com:8888"); // 开发者密钥,此项必填,由捷通华声提供 initparam.addParam (InitParam.AuthParam.PARAM_KEY_DEVELOPER_KEY, "01234567890"); // 应用程序序号,此项必填,由捷通华声提供 initparam.addParam (InitParam.AuthParam.PARAM_KEY_APP_KEY, "1234abcd"); // 日志的路径,可选,如果不传或者为空则不生成日志 String logDirPath = "/storage/emulated/0/sinovoice/com.sinovoice. example/log"; initparam.addParam (InitParam.LogParam.PARAM_KEY_LOG_FILE_PATH, logDirPath); //日志等级,0=无,1=错误,2=警告,3=信息,4=细节,5=调试 //SDK将输出小于等于logLevel的日志信息 initparam.addParam (InitParam.LogParam.PARAM_KEY_LOG_LEVEL, "5"); // 灵云系统初始化 // 第二个参数在Android平台下,必须为当前的Context int errCode = HciCloudSys.hciInit (initparam.getStringConfig(), this); if(errCode != HciErrorCode.HCI_ERR_NONE) { // "系统初始化失败" return; }
3.1.2 授权检测
检查相应能力的授权是否有效。SDK提供了 HciCloudSys.hciCheckAuth
和 HciCloudSys.hciGetAuthExpireTime
两个函数,分别来检查授权合法性和授权截止时间。
在初始化灵云SDK的通用模块后,还需要调用授权检测函数获取云端授权。
Android示例代码
// 获取授权 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()) { 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; } }
3.2 TTS语音合成
3.2.1 云端能力语音合成
3.2.1.1 TTS 能力初始化
云端能力的capkey都是以tts.cloud
开头。云端能力可以选择多个不同的播音员和语种,详见灵云SDK开发手册。在集成过程中如有疑问,可登录灵云开发者论坛。
Android代码
TtsInitParam ttsInitParam = new TtsInitParam(); // 能力初始化时可以设置多个capkey,以分号间隔。 ttsInitParam.addParam(TtsInitParam.PARAM_KEY_INIT_CAP_KEYS, "tts.cloud.xixi;tts.cloud.xiaokun"); // ................ int errCode = HciCloudTts.hciTtsInit(ttsInitParam.getStringConfig()); if (errCode != HciErrorCode.HCI_ERR_NONE) { ShowMessage("hciTtsInit error:" + HciCloudSys.hciGetErrorInfo(errCode)); return; } else { ShowMessage("hciTtsInit Success"); }
3.2.1.2 创建TTS Session
Android代码
TtsConfig sessionConfig = new TtsConfig(); // 此capkey必须在HciCloudTts.hciTtsInit时已经初始化的范围内 sessionConfig.addParam(TtsConfig.SessionConfig.PARAM_KEY_CAP_KEY, "tts.cloud.xixi"); Session session = new Session(); int errCode = HciCloudTts.hciTtsSessionStart(sessionConfig.getStringConfig(), session); if (HciErrorCode.HCI_ERR_NONE != errCode) { ShowMessage("hciTtsSessionStart error:" + HciCloudSys.hciGetErrorInfo(errCode)); return; }
3.2.1.3 TTS 合成
Android代码
int errCode = HciCloudTts.hciTtsSynthEx(session, synthData, "", mTtsSynthCallback); if (errCode == HciErrorCode.HCI_ERR_NONE) { ShowMessage("hciTtsSynth Success"); }else{ ShowMessage("hciTtsSynth error:" + HciCloudSys.hciGetErrorInfo(errCode)); }
合成函数的第三个参数用来设置合成效果,不用时可以为空。和前面一样,设置参数以“字段=值”形式的字符串的方式给出。用户可以设置合成音量、语速、标点符号读法、数字读法等,详见灵云SDK开发手册。
第四个参数是接收合成结果的回调函数。当有合成结果时,SDK会触发回调函数,并把合成结果以TtsSynthResult形式传递给用户。回调的处理示例如下:
Android代码
/** * 引擎合成过程中,每合成一段文字都会调用该回调方法通知外部并传回音频数据 音频数据保存在对象 * TtsSynthResult中,通过该对象的getVoieceData()方法可以获取 合成是设定音频格式的音频数据 */ private static ITtsSynthCallback mTtsSynthCallback = new ITtsSynthCallback() { @Override public boolean onSynthFinish(int errorCode, TtsSynthResult result) { // errorCode 为当前合成操作返回的错误码,如果返回值为HciErrorCode.HCI_ERR_NONE则表示合成成功 if (errorCode != HciErrorCode.HCI_ERR_NONE) { Log.e(TAG, "synth error, code = " + errorCode); return false; } // 此处可以使用方法 // result.getCurrentSynthText() 来获取当前合文本 // 可以使用方法 // result.getVoiceData() 来获取合成的音频数据,一般直接用来播放,也可以保存到本地的音频文件中 if (mFos == null) { // 合成数据保存在外部存储/synth 文件中 initFileOutputStream(); } // 将本次合成的数据写入文件 // 每次合成的数据,可能不是需要合成文本的全部,需要多次写入 if(result != null && result.getVoiceData() != null){ int length = result.getVoiceData().length; if (length > 0) { try { mFos.write(result.getVoiceData(), 0, length); mFos.flush(); } catch (IOException e) { e.printStackTrace(); } } } if (!result.isHasMoreData()) { flushOutputStream(); } //mark 标记语言的回调结果 if(result.getTtsSynthMark().size() > 0) { for(int i=0;i<result.getTtsSynthMark().size();++i) { ShowMessage(result.getTtsSynthMark().get(i).toStirng()); } } // 返回true表示处理结果成功,通知引擎可以继续合成并返回下一次的合成结果; 如果不希望引擎继续合成, 则返回false // 该方法在引擎中是同步的,即引擎会持续阻塞一直到该方法执行结束 return true; } };
合成普通文本
以合成“8月27日,在第十三届全国运动会即将开幕之际,中共中央总书记、国家主席、中央军委主席习近平在天津会见全国群众体育先进单位、先进个人代表和全国体育系统先进集体、先进工作者代表以及在本届全运会群众比赛项目中获奖的运动员代表,并发表重要讲话。”这样一段文本为例,合成过程如下:
SDK按照TtsSynthResult对象中对应的文字提供音频数据。
合成s3ml文本 合成文本如下:
<?xml version="1.0" encoding="GB18030"?>
<speak version="1.0" xml:lang="zh-cn">
从这里<mark name="here"/>开始出发,到那里<mark name="there"/>停止。
</speak>
此时返回结果中会有mark节点信息,见下图
注: 要合成s3ml文本,必须设置 tagmode 参数。示例代码如下:
Android代码
TtsConfig synthConfig = new TtsConfig(); synthConfig.addParam(TtsConfig.BasicConfig.PARAM_KEY_TAG_MODE, "s3ml"); HciCloudTts.hciTtsSynthEx(session, synthData, synthConfig.getStringConfig(), mTtsSynthCallback);
3.2.1.4 释放 Session
Android代码
HciCloudTts.hciTtsSessionStop(session);
3.2.2 本地能力语音合成
和云端能力不同,本地TTS能力必须提供本地音库资源文件才能工作。因此有两个重要参数:dataPath
与resPrefix
。dataPath
参数在TTS能力初始化时,指定本地资源的根目录;resPrefix
参数可进一步指定使用哪一种具体音色。
3.2.2.1 TTS 能力初始化
本地能力的 capkey 只有 tts.local.synth 和 tts.local.synth.v9 两种,用户通过提供不同的音库文件来选择生成不同的音频效果。音库文件的明细请参考灵云开发手册。这两种能力的使用方式均一致,不同的地方在于使用不同的库,参见2. TTS 能力使用说明中对库文件的介绍。在调用初始化方法之前,确保本地资源已拷贝到dataPath
目录下。
示例代码如下:
Android代码
//TTS初始化 TtsInitParam ttsInitParam = new TtsInitParam(); String dataPath = Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator + "sinovoice" + File.separator + context.getPackageName() + File.separator + "data"; ttsInitParam.addParam(TtsInitParam.PARAM_KEY_DATA_PATH, dataPath); copyAssetsFiles(context, dataPath); ttsInitParam.addParam(TtsInitParam.PARAM_KEY_INIT_CAP_KEYS, capkey); Log.i(TAG, "hciTtsInit config :" + ttsInitParam.getStringConfig()); // 调用初始化方法 int errCode = HciCloudTts.hciTtsInit(ttsInitParam.getStringConfig()); if (errCode != HciErrorCode.HCI_ERR_NONE) { ShowMessage("hciTtsInit error:" + HciCloudSys.hciGetErrorInfo(errCode)); return; } else { ShowMessage("hciTtsInit Success"); }
本地资源文件的说明请参考灵云SDK开发手册。
3.2.2.2 创建 TTS Session
Android代码
//启动TTS会话 TtsConfig sessionConfig = new TtsConfig(); sessionConfig.addParam(TtsConfig.SessionConfig.PARAM_KEY_CAP_KEY, capkey); Session session = new Session() // 使用资源前缀来区分datapath下不同的音库资源 // 这里指定为"WangJing_Common/", 则会在datapath/WangJing_Common/下加载资源 sessionConfig.addParam(TtsConfig.SessionConfig.PARAM_KEY_RES_PREFIX, "WangJing_Common/"); int errCode = HciCloudTts.hciTtsSessionStart(sessionConfig.getStringConfig(), session); if (HciErrorCode.HCI_ERR_NONE != errCode) { ShowMessage("hciTtsSessionStart error:" + HciCloudSys.hciGetErrorInfo(errCode)); return; } ShowMessage("hciTtsSessionStart Success");
当在一个应用中使用多种音库时,可通过TtsConfig.SessionConfig.PARAM_KEY_RES_PREFIX配置项便文件前缀来区分不同音库。例如,当前有Barron音库与XiaoKun_Common音库,这两个音库下的资源名称都是一样的,可通过以下两种方式配置不同的session,合成不同音色的声音。
- 将Barron和XiaoKunCommon音库的文件分别手动加上"Barron”和"XiaoKunCommon"前缀, 放置在datapath路径下, Session A 使用 "resPrefix=Barron", Session B使用 "resPrefix=XiaoKun_Common";
- 将Barron和XiaoKun_Common音库的文件分别放在Barron文件夹与XiaoKun_Common文件夹下,将这两个文件夹放置在datapath路径下,Session A 使用 "resPrefix=Barron/", Session B使用 "resPrefix=XiaoKun_Common/" ;本示例采用此方法。
3.2.2.3 TTS 合成
和3.2.1.3节相同
3.2.2.4 释放 Session
和3.2.1.4节相同
3.3 灵云系统反初始化
Android代码
HciCloudTts.hciTtsRelease(); HciCloudSys.hciRelease();
4. 常用配置说明
- 数字读法 配置数字的读法,例如“1200”是读成“一千二百”还是“一二零零”。通过digitMode配置
- 姓氏多音字 配置例如“单、仇”这样的字在作为姓氏时的特殊读法,通过namePolyphone配置(仅本地)
- 1、2的特殊读法 是读成“一”、“二”还是读成“幺”、“两”,通过specialOne和specialTwo配置(仅本地)
- 背景音配置 通过backaudiopath配置(仅本地)
- 输出音频格式 通过 audioFormat 配置 其他请参考灵云SDK开发手册。
5. FAQ
Q1: 你们的tts,中文是否支持方言播报,如播报湖南话、四川话等。
A: 方言仅支持粤语,shuyi 音库。其他方言语种,不支持。
Q2: 我用的是官网下载的xiaokun音库,我发现她播报英文的时候,是一个字一个字去播放的。试过了engMode=auto,english两个参数,依然如此。
A: Xiaokun音库适合播放中文,不适合播放英文。Xiaokun音库里自带的英文资源EnPackage.dat读英文就是这个效果。如果希望体验纯正的美式英语,建议下载一个原生英语音库(如Cameal),再将Cameal音库下的EnPackage.dat文件替换您的工程中的Xiaokun音库的同名文件。Cameal音库读英文,效果就自然多了。
Q3:我用你们的ttsplayerexample进行测试,capkey=tts.cloud.wangjing,服务器返回11号错误:service response failed。我除了填入账号信息,什么配置都没有修改,这个可能是什么原因?
A: 经检查,您将我们示例程序中的tts.txt这个文件的文本格式修改为了GBK,默认应为UTF8。我们的TTS输入的合成文本格式必须是UTF8。
Q4:我运行你们的tts_example,发现合成结果保存到了一个synth.pcm中,我要怎么才能播放这个文件?你们是否支持实时播放的方式?
A: tts如果您不使用播放器,直接调用tts_synth接口,结果是保存到一个文件,默认是16k16bit pcm。这个文件无法使用windows media player打开,可以用cooledit打开此文件。我们的tts支持实时播放的方式,播放器相关的代码封装在hcicloud_player-8.1.jar中。您运行我们的ttsplayerexample示例程序,可以体验。
Q5: 你们的ttsplayer,是否支持自定义输出的音频流类型?我看你们的示例程序,默认是在扬声器里输出。如果我希望在听筒、蓝牙音轨中进行输出,应该如何操作?
A: 支持,hcicloud_player-8.1.jar中,封装了对应的方法。播放器播放,mTtsPlayer.play(text,ttsConfig.getStringConfig(),mStreamtype),其中第三个参数即为音频流类型,这个参数值在车机项目上等,常常存在自定义的情况。我们的mStreamtype传入的是int型,各参数对应值默认如下,比如听筒输出的streamtype=0。
Q6: 我如何可以得知播放器的状态和当前是否可以停止播放?你们的状态回调和错误回调,都是走的同一个回调函数吗?
A: 通过hci_tts_player_get_state ( )接口获取播放器状态,通过tts_player_can_stop ( ) 接口查询是否当前是否可以停止播放器。 我们的播放器,有事件回调、进度回调、错误回调等,都是分别在不同的结构体里定义的。比如事件回调是Callback_PlayerEventStateChange,进度回调PlayerEventProgressChange,错误回调Callback_PlayerEventPlayerError。iOS系统还有一个专门的Callback_PlayerSetAudioSession。
Q7: 我看你们tts本地播放的capkey是tts.local.synth,这个和音库有关系吗?xiaokun,wangjing,xixi等音库,都可以用这个capkey吗?
A: tts本地能力的capkey都是tts.local.synth,云端能力才指定发音人,如tts.cloud.wangjing,tts.cloud.xixi,本地能力,最终用什么音库发声,取决于代码datapath指定路径下,组入的是什么音库的资源文件。比如,组入的是wangjing的本地资源,那么发声人就是wangjing。
Q8: 你们的tts,我调到最大声,发现声音还是有些小,在我们的车机上使用,大概低了6db左右。这个还有办法增加吗?
A: 首先确认ttsconfig的volume参数值已调到最大值9.99,音量仍不足的情况下,尝试调整gainfactor增益参数(取值范围:【2,8】),可以继续增大音量。但是gainfactor参数调节太大,可能会造成破音。详见开发手册。
Q9: 你们的tts,都支持合成为哪些格式的音频文件?
A: 支持合成为pcm8k16bit,pcm16k16bit,alaw8k8bit,ulaw8k8bit等音频格式,云端支持为MP3格式,详见开发手册。文件可以保存为.wav结尾,但是并不是windows wav,使用wma无法直接打开。建议使用cooledit等音频编解码工具进行播放。