前言
最近项目需要接入语音评测功能,公司有做过这方面的同事推荐了科大讯飞语音评测,于是根据官网的开发指南接入了sdk,可以成功评测用户的口语能力,并给出合适的分数,但是期间遇到了很多小问题,于是写在这篇文章记录一下开发及填坑的过程。
正文
1.接入sdk:
如何接入sdk请去看科大讯飞官网提供的接入指南,这里就不做介绍了
传送门:https://doc.xfyun.cn/msc_android/%E8%AF%AD%E9%9F%B3%E8%AF%84%E6%B5%8B.html
2.编写语音评测工具类:
因为有两个地方用到了这个评测功能,所以为了使用方便,写了一个工具类,直接上代码:
/**
* @ClassName: SpeechEvaluatorUtil
* @Desciption: //语音评测工具类
* @author: jesse
* @date: 2018-06-29
*/
public class SpeechEvaluatorUtil {
private static final String TAG = SpeechEvaluatorUtil.class.getSimpleName();
public static final String EVA_RECORD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/msc/ise.wav";
private static SpeechEvaluator mIse;
public static void init(Context context) {
if (mIse == null) {
mIse = SpeechEvaluator.createEvaluator(context, null);
}
}
/**
* @param evaText 评测用句
* @param mEvaluatorListener 语音评测回调接口
* @return 评测录音存储路径
*/
public static void startSpeechEva(String evaText, EvaluatorListener mEvaluatorListener) {
setParams();
// 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
// 注:AUDIO_FORMAT参数语记需要更新版本才能生效
mIse.startEvaluating(evaText, null, mEvaluatorListener);
}
//通过写入音频文件进行评测
public static void startEva(byte[] audioData,String evaText,EvaluatorListener mEvaluatorListener){
setParams();
//通过writeaudio方式直接写入音频时才需要此设置
mIse.setParameter(SpeechConstant.AUDIO_SOURCE,"-1");
int ret = mIse.startEvaluating(evaText, null, mEvaluatorListener);
//在startEvaluating接口调用之后,加入以下方法,即可通过直接
//写入音频的方式进行评测业务
if (ret != ErrorCode.SUCCESS) {
Log.i(TAG,"识别失败,错误码:" + ret);
} else {
if(audioData != null) {
//防止写入音频过早导致失败
try{
new Thread().sleep(100);
}catch (InterruptedException e) {
Log.d(TAG,"InterruptedException :"+e);
}
mIse.writeAudio(audioData,0,audioData.length);
mIse.stopEvaluating();
}else{
Log.i(TAG,"audioData == null");
}
}
}
private static void setParams() {
Log.i(TAG, "setParams()");
// 设置评测语种:英语
mIse.setParameter(SpeechConstant.LANGUAGE, "en_us");
// 设置评测题型:句子
mIse.setParameter(SpeechConstant.ISE_CATEGORY, "read_sentence");
mIse.setParameter(SpeechConstant.RESULT_LEVEL,"plain");
mIse.setParameter(SpeechConstant.ISE_AUDIO_PATH, EVA_RECORD_PATH);
mIse.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
}
//停止评测
public static void stopSpeechEva() {
if (mIse.isEvaluating()) {
mIse.stopEvaluating();
}
}
//取消评测
public static void cancelSpeechEva() {
mIse.cancel();
}
}
这里写了两种评测方式:
第一种是“直接根据mic录到的音频进行评测”(startSpeechEva()),这种方式会在EVA_RECORD_PATH路径下生成一个约44kb的wav格式音频文件,但是这里有一个巨大的坑--音频文件不会立即刷新覆盖上一次录音,大概会延迟0.7-1.2s的时间,这样就造成了一个问题:如果想要录音完后立即播放这次的录音的话,会发现播放的录音是上一次的录音!而很不巧,我就需要做这样的一个功能,所以我弃用了第一种方式,改用了第二种方式。
第二种是“先自己把音频录下来,生成wav格式文件,然后再转换成byte数组进行评测”(startEva()),这种方式因为是自己录音,所以没有刷新录音文件的延迟,可以实现录音完后立即播放录音音频的效果,这就解决了第一种方式里的大坑。但是还有个坑就是,较之第一种方式,这种方式的评分偏低很多(第一种方式能得90分的发音,第二种方式大概得70分)。如果有人能够解决这个坑的话,希望你能给我留言告知一下方法。(此坑已填,文中代码已修改)
3.编写录音工具类
这里我写了两个工具类,一个用的是MediaRecorder进行录音,一个是用AudioRecord,第2步里的第二种方式用到的是AudioRecorder这个工具类。这里两种都奉上。
MediaRecorder工具类:
/**
* @ClassName: MediaRecordUtil
* @Desciption: //录音工具类
* @author: jesse
* @date: 2018-06-15
*/
public class MediaRecordUtil {
//文件路径
private String filePath;
//文件夹路径
private String FolderPath;
private MediaRecorder mMediaRecorder;
private final String TAG = MediaRecordUtil.class.getSimpleName();
public static final int MAX_LENGTH = 1000 * 60 * 10;// 最大录音时长1000*60*10;
private OnAudioStatusUpdateListener audioStatusUpdateListener;
/**
* 文件存储默认sdcard/record
*/
public MediaRecordUtil(){