(本Demo的开发环境为Android Studio)。
转载自:http://blog.csdn.net/highboys/article/details/52145038
一、语音识别
1.下载SDK(地址:
http://www.xfyun.cn/sdk/dispatcher
),选择语音听写SDK(如下图) ,下载前会让你先创建应用,创建应用后会得到一个appid。然后点“立即开通”去开通“语音识别”功能,之后就会跳出“SDK下载”的页面,然后就可以下载了(未注册账号的要先注册一个账号)。
2.将下载好的SDK中 libs 目录下的 Msc.jar包引入到工程中(参见
http://blog.csdn.net/highboys/article/details/51549679
,此外,因为本Demo中会用到json的东西,所以还得自己去网上下一个Gson的jar包,一并引进去)。之后在main目录下新建一个jniLibs目录,将 SDK中libs 目录下的armeabi 拷进去,如下图所示(第④个先不用管):
3.科大讯飞为我们提供了一套语音听写时的UI,即听写的时候会有一个动画效果(如下图),这个时候我们需要 先将 SDK 资源包 assets 路径下的资源文件拷贝至 Android 工程asstes 目录下(没有的话自己新建),参照第2步图的④。
4.接下来就是代码的实现了。首先在Manifest中添加一下权限
<!--连接网络权限,用于执行云端语音能力 -->
<uses-permission android:name="android.permission.INTERNET" />
<!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!--读取网络信息状态 -->
<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" />
<!--SD卡读写的权限(如果需要保存音频文件到本地的话)-->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
其次是java代码(逻辑上是点击了某个Button之后,才执行下面的代码)。
//有动画效果
private RecognizerDialog iatDialog;
// ①语音配置对象初始化
SpeechUtility.createUtility(MainActivity.this, SpeechConstant.APPID + "=578f1af7");//将这里的578f1af7替换成自己申请得到的8位appid
// ②初始化有交互动画的语音识别器
iatDialog = new RecognizerDialog(MainActivity.this, mInitListener);
//③设置监听,实现听写结果的回调
iatDialog.setListener(new RecognizerDialogListener() {
String resultJson = "[";//放置在外边做类的变量则报错,会造成json格式不对(?)
@Override
public void onResult(RecognizerResult recognizerResult, boolean isLast) {
System.out.println("----------------- onResult -----------------");
if (!isLast) {
resultJson += recognizerResult.getResultString() + ",";
} else {
resultJson += recognizerResult.getResultString() + "]";
}
if (isLast) {
//解析语音识别后返回的json格式的结果
Gson gson = new Gson();
List<DictationResult> resultList = gson.fromJson(resultJson,
new TypeToken<List<DictationResult>>() {
}.getType());
String result = "";
for (int i = 0; i < resultList.size() - 1; i++) {
result += resultList.get(i).toString();
}
etText.setText(result);
//获取焦点
etText.requestFocus();
//将光标定位到文字最后,以便修改
etText.setSelection(result.length());
}
}
@Override
public void onError(SpeechError speechError) {
//自动生成的方法存根
speechError.getPlainDescription(true);
}
});
//开始听写,需将sdk中的assets文件下的文件夹拷入项目的assets文件夹下(没有的话自己新建)
iatDialog.show();
其中的mInitListener定义如下:(此处写在方法外即可)
public static final String TAG = "MainActivity";
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
Log.d(TAG, "SpeechRecognizer init() code = " + code);
if (code != ErrorCode.SUCCESS) {
Toast.makeText(MainActivity.this, "初始化失败,错误码:" + code, Toast.LENGTH_SHORT).show();
}
}
};
上面的代码用到了一个DictationResult类(一个用来接收转换 语音听写结果的类),需要自己新建,定义如下
/**
* 解析 语音听写返回结果Json格式字符串 的模板类(多重嵌套Json)
*
* 语音识别结果Json数据格式(单条数据):
* {"sn":1,"ls":true,"bg":0,"ed":0,"ws":[
* {"bg":0,"cw":[{"w":"今天","sc":0}]},
* {"bg":0,"cw":[{"w":"的","sc":0}]},
* {"bg":0,"cw":[{"w":"天气","sc":0}]},
* {"bg":0,"cw":[{"w":"怎么样","sc":0}]},
* {"bg":0,"cw":[{"w":"。","sc":0}]}
* ]}
*
* sn number :第几句
* ls boolean: 是否最后一句
* bg number :开始
* ed number :结束
* ws array :词
* cw array :中文分词
* w string :单字
* sc number :分数
*/
public class DictationResult {
private String sn;
private String ls;
private String bg;
private String ed;
private List<Words> ws;
public static class Words {
private String bg;
private List<Cw> cw;
public static class Cw {
private String w;
private String sc;
public String getW() {
return w;
}
public void setW(String w) {
this.w = w;
}
public String getSc() {
return sc;
}
public void setSc(String sc) {
this.sc = sc;
}
@Override
public String toString() {
return w;
}
}
public String getBg() {
return bg;
}
public void setBg(String bg) {
this.bg = bg;
}
public List<Cw> getCw() {
return cw;
}
public void setCw(List<Cw> cw) {
this.cw = cw;
}
@Override
public String toString() {
String result = "";
for (Cw cwTmp : cw) {
result += cwTmp.toString();
}
return result;
}
}
public String getSn() {
return sn;
}
public void setSn(String sn) {
this.sn = sn;
}
public String getLs() {
return ls;
}
public void setLs(String ls) {
this.ls = ls;
}
public String getBg() {
return bg;
}
public void setBg(String bg) {
this.bg = bg;
}
public String getEd() {
return ed;
}
public void setEd(String ed) {
this.ed = ed;
}
public List<Words> getWs() {
return ws;
}
public void setWs(List<Words> ws) {
this.ws = ws;
}
@Override
public String toString() {
String result = "";
for (Words wsTmp : ws) {
result += wsTmp.toString();
}
return result;
}
}
如此,便可实现语音听写了。这个过程可能会遇到各种各样的问题,具体错误码参见 http://www.xfyun.cn/doccenter/faq
5.说完了有动画效果的,下面就来说说没有动画效果的(也比较简单)。
//1.创建SpeechRecognizer对象,第二个参数:本地识别时传InitListener
SpeechRecognizer mIat = SpeechRecognizer.createRecognizer(context, null);
//2.设置听写参数,详见SDK中《MSC Reference Manual》文件夹下的SpeechConstant类
mIat.setParameter(SpeechConstant.DOMAIN,"iat");
mIat.setParameter(SpeechConstant.LANGUAGE,"zh_cn");
mIat.setParameter(SpeechConstant.ACCENT,"mandarin ");
//保存音频文件到本地(有需要的话) 仅支持pcm和wav,且需要自行添加读写SD卡权限
mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/mIat.wav");
// 3.开始听写
mIat.startListening(mRecoListener);
//听写监听器
private RecognizerListener mRecoListener = new RecognizerListener() {
//听写结果回调接口(返回Json格式结果,用户可参见附录13.1);
// 一般情况下会通过onResults接口多次返回结果,完整的识别内容是多次结果的累加;
// 关于解析Json的代码可参见Demo中JsonParser类;
// isLast等于true时会话结束。
public void onResult(RecognizerResult results, boolean isLast) {
Log.d(TAG, "result:" + results.getResultString());
}
//会话发生错误回调接口
public void onError(SpeechError error) {
//打印错误码描述
Log.d(TAG, "error:" + error.getPlainDescription(true))
}
//开始录音
public void onBeginOfSpeech() {
}
// volume音量值0~30,data音频数据
public void onVolumeChanged(int volume, byte[] data) {
}
//结束录音
public void onEndOfSpeech() {
}
//扩展用接口
public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
}
};
可以看到上面的onResult回调方法跟有动画效果时的onResult回调方法是一样的,所以主要的处理还是在这个方法中,将有动画的那个onResult回调中的代码拷过来就行了。如此,简单的语音听写功能就实现了。
效果图:
二、语音合成
1.语音合成也需要创建语音配置对象:
SpeechUtility.createUtility(MainActivity.this, SpeechConstant.APPID + "=578f1af7");
2.创建用于语音合成的对象并设置参数,进行语音合成
//1.创建SpeechSynthesizer对象, 第二个参数:本地合成时传InitListener
SpeechSynthesizer mTts = SpeechSynthesizer.createSynthesizer(MainActivity.this, null);
/**
2.合成参数设置,详见《科大讯飞MSC API手册(Android)》SpeechSynthesizer 类
*/
// 清空参数
mTts.setParameter(SpeechConstant.PARAMS, null);
mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD); //设置云端
mTts.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");//设置发音人
mTts.setParameter(SpeechConstant.SPEED, "50");//设置语速
//设置合成音调
mTts.setParameter(SpeechConstant.PITCH, "50");
mTts.setParameter(SpeechConstant.VOLUME, "80");//设置音量,范围0~100
mTts.setParameter(SpeechConstant.STREAM_TYPE, "3");
// 设置播放合成音频打断音乐播放,默认为true
mTts.setParameter(SpeechConstant.KEY_REQUEST_FOCUS, "true");
// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
// 注:AUDIO_FORMAT参数语记需要更新版本才能生效
// mTts.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
// boolean isSuccess = mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/tts2.wav");
// Toast.makeText(MainActivity.this, "语音合成 保存音频到本地:\n" + isSuccess, Toast.LENGTH_LONG).show();
//3.开始合成
int code = mTts.startSpeaking("在这里放置需要进行合成的文本", mSynListener);
if (code != ErrorCode.SUCCESS) {
if (code == ErrorCode.ERROR_COMPONENT_NOT_INSTALLED) {
//上面的语音配置对象为初始化时:
Toast.makeText(MainActivity.this, "语音组件未安装", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "语音合成失败,错误码: " + code, Toast.LENGTH_LONG).show();
}
}
合成时需要一个SynthesizerListener类型的合成监听器作参数,定义如下
//合成监听器
private SynthesizerListener mSynListener = new SynthesizerListener() {
//会话结束回调接口,没有错误时,error为null
public void onCompleted(SpeechError error) {
}
//缓冲进度回调
//percent为缓冲进度0~100,beginPos为缓冲音频在文本中开始位置,endPos表示缓冲音频在文本中结束位置,info为附加信息。
public void onBufferProgress(int percent, int beginPos, int endPos, String info) {
}
//开始播放
public void onSpeakBegin() {
}
//暂停播放
public void onSpeakPaused() {
}
//播放进度回调
//percent为播放进度0~100,beginPos为播放音频在文本中开始位置,endPos表示播放音频在文本中结束位置.
public void onSpeakProgress(int percent, int beginPos, int endPos) {
}
//恢复播放回调接口
public void onSpeakResumed() {
}
//会话事件回调接口
public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {
}
};
本文参照SDK中doc文件夹下的
MSC Develop Manual for Android.pdf
文件完成,里边写得很详细。也可参见
http://www.xfyun.cn/doccenter/awd
。
效果:
错误码
其它错误码参见下表:
错误码 | 数值 | 含义 |
ERROR_NO_NETWORK | 20001 | 无有效的网络连接 |
ERROR_NETWORK_TIMEOUT | 20002 | 网络连接超时 |
ERROR_NET_EXPECTION | 20003 | 网络连接发生异常 |
ERROR_INVALID_RESULT | 20004 | 无有效的结果 |
ERROR_NO_MATCH | 20005 | 无匹配结果 |
ERROR_AUDIO_RECORD | 20006 | 录音失败 |
ERROR_NO_SPPECH | 20007 | 未检测到语音 |
ERROR_SPEECH_TIMEOUT | 20008 | 音频输入超时 |
ERROR_EMPTY_UTTERANCE | 20009 | 无效的文本输入 |
ERROR_FILE_ACCESS | 20010 | 文件读写失败 |
ERROR_PLAY_MEDIA | 20011 | 音频播放失败 |
ERROR_INVALID_PARAM | 20012 | 无效的参数 |
ERROR_TEXT_OVERFLOW | 20013 | 文本溢出 |
ERROR_INVALID_DATA | 20014 | 无效数据 |
ERROR_LOGIN | 20015 | 用户未登陆 |
ERROR_PERMISSION_DENIED | 20016 | 无效授权 |
ERROR_INTERRUPT | 20017 | 被异常打断 |
ERROR_VERSION_LOWER | 20018 | 版本过低 |
ERROR_COMPONENT_NOT_INSTALLED | 21001 | 没有安装语音组件 |
ERROR_ENGINE_NOT_SUPPORTED | 21002 | 引擎不支持 |
ERROR_ENGINE_INIT_FAIL | 21003 | 初始化失败 |
ERROR_ENGINE_CALL_FAIL | 21004 | 调用失败 |
ERROR_ENGINE_BUSY | 21005 | 引擎繁忙 |
ERROR_LOCAL_NO_INIT | 22001 | 本地引擎未初始化 |
ERROR_LOCAL_RESOURCE | 22002 | 本地引擎无资源 |
ERROR_LOCAL_ENGINE | 22003 | 本地引擎内部错误 |
ERROR_IVW_INTERRUPT | 22004 | 本地唤醒引擎被异常打断 |
ERROR_UNKNOWN | 20999 | 未知错误 |
合成发音人列表
- 语言为中英文的发音人可以支持中英文的混合朗读。
- 英文发音人只能朗读英文,中文无法朗读。
- 汉语发音人只能朗读中文,遇到英文会以单个字母的方式进行朗读。
- 使用新引擎参数会获得更好的合成效果。
名称 | 属性 | 语言 | 参数名称 | 新引擎参数 | 备注 |
小燕 | 青年女声 | 中英文 (普通话) | xiaoyan | 默认 | |
小宇 | 青年男声 | 中英文 (普通话) | xiaoyu | ||
凯瑟琳 | 青年女声 | 英文 | catherine | ||
亨利 | 青年男声 | 英文 | henry | ||
玛丽 | 青年女声 | 英文 | vimary | ||
小研 | 青年女声 | 中英文 (普通话) | vixy | ||
小琪 | 青年女声 | 中英文 (普通话) | vixq | xiaoqi | |
小峰 | 青年男声 | 中英文 (普通话) | vixf | ||
小梅 | 青年女声 | 中英文 (粤语) | vixm | xiaomei | |
小莉 | 青年女声 | 中英文 (台湾普通话) | vixl | ||
晓琳 | 青年女声 | 中英文 (台湾普通话) | xiaolin | ||
小蓉 | 青年女声 | 汉语 (四川话) | vixr | xiaorong | |
小芸 | 青年女声 | 汉语 (东北话) | vixyun | ||
小倩 | 青年女声 | 汉语 (东北话) | xiaoqian | ||
小坤 | 青年男声 | 汉语 (河南话) | vixk | xiaokun | |
小强 | 青年男声 | 汉语 (湖南话) | vixqa | xiaoqiang | |
小莹 | 青年女声 | 汉语 (陕西话) | vixying | ||
小新 | 童年男声 | 汉语 (普通话) | vixx | xiaoxin | |
楠楠 | 童年女声 | 汉语 (普通话) | vinn | nannan | |
老孙 | 老年男声 | 汉语 (普通话) | vils | ||
Mariane | 法语 | Mariane | |||
Allabent | 俄语 | Allabent | |||
Gabriela | 西班牙语 | Gabriela | |||
Abha | 印地语 | Abha | |||
XiaoYun | 越南语 | XiaoYun |