语音交互的响应快慢直接影响用户体验,用户希望的是又快又准,即语音识别要准确及响应要快速。语音交互的整个过程:用户唤醒语音交互->APP启动录音->上传音频数据(边录边传)->上报数据传输完毕->平台语音识别及解析->把结果反馈给APP;文本主要讨论影响语音交互响应快慢的因素。
一、录音采集最短时间和最长时间
音频采集要考虑众多因素来设置录音最短时间和最长时间,当然有些平台会直接限制的(阅读平台资料可以获取到)。
录音采集最短时间:考虑你可能按下语音键,还没说话,就结束录音采集;
录音采集最长时间:不可能无限制提交数据,根据经验(大数据)设置。
录音采集最短时间是会直接影响语音交互响应的参数,一般最长时间是不什么会的。
//录音必须持续时间
private static final int RECORD_DURATION_TIME = 3 * 1000;
//录音超时时间
private final static long RECORD_TIMEOUT_MILLIS = 15 * 1000;
Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what == WHAT_RECORD_TIMEOUT) {
isRecordTimeout = true;
//ExpectSpeech timeoutInMilliseconds
if (expectSpeechTimeoutInMilliseconds != 0) { //发送ExpectSpeechTimedOut
sendExpectSpeechTimedOutEvent();
}
if (mBtDeviceSpeechType == BT_DEVICE_SPEECH_RECOGNITION) {
mBtDeviceSpeechType = BT_DEVICE_SPEECH_RECOGNITION_NONE;
stopBluetoothSCO();
}
stopListening(true);
} else if (msg.what == WHAT_RECORD_DURATION_TIME) {
mRecordDurationReached = true;
} else if (msg.what == WHAT_WAITING_VOICE_REPLY_COMPLETE_TIME) {
mDialogRequestId = "0000";//代表取消上一条语音交互结果
waitingVoiceReplyComplete = true;
if (mForbidInteraction) {//打电话中,禁止一切交互。
return;
}
SoundUtils soundUtils = SoundUtils.getInstance(mAppContext);
soundUtils.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
checkChannelActive();
}
});
soundUtils.playSound(SoundUtils.SOUND_CANT_HEART_IT, false);
} else if (msg.what == WHAT_PLAYBACKSTARTEDEVENT) {
if (audioPlayer != null && audioPlayer.isPlaying() && mDownChannelService != null) {//mDownChannelService != null说明app没被退出
sendPlaybackStartedEvent4AudioPlayer(audioPlayer.getCurrentItem(), audioPlayer);
}
}
}
};
二、数据段上传时间
数据段上传是指在边录边传的情况下,每次上传多长的录音时间。
普及下边录边传概念,在语音交互中有两种方式提交录音的音频流数据到后台:一种是等录音全部完成后再提交数据,这种一般在时时性不高的场景,如儿童故事机;另一种就是边录边传,一边录音,一边提交录音音频流数据。
回过头来,每次上传多长的录音时间又是一个矛盾的参数,在同样大小的音频数据流中,时间片设置短了,每次提交的数据是少了,但提交的次数却多了起来;时间片设置过长,虽然上传次数少了,但因每次提交的数据量比较大,也会引起其他问题(如平台的处理、堵塞等),所以这个100ms的时间是要不断优化的。
private DataRequestBody getDataRequestBody() {
return new DataRequestBody() {
@Override
public void writeTo(BufferedSink sink) throws IOException {
while (recorder != null && recorder.getState() == AudioRecorder.State.RECORDING && !isRecordTimeout) {
if (sink != null && recorder != null) {
sink.write(recorder.consumeRecording());
sink.flush();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mHandler.removeMessages(WHAT_RECORD_TIMEOUT);
}
};
}
三、VAD时间
VAD使用短时能量判断规则,短时能量可以直接反应出语音信号能量及幅度大小,进而可以对有声段和无声段进行判断,数据分析处理软件APP动态寻找每个帧与之前的音频帧中的最大能量值。
音频信号是一个非稳态、时变的信号,为了取得更准确的计算结果,我们认为其在“短时间”范围内是稳态、时不变的,这个时间, 一般数据分析处理软件APP将每一帧的音频信号的持续时间设置为10ms。
private long getRms(int end, int span) {
int begin = end - span;
if (begin < 0) {
begin = 0;
}
if (begin % 2 != 0) {
begin++;
}
long sum = 0;
for (int i = begin; i < end; i += 2) {
short curSample = getShort(this.mRecording[i], this.mRecording[i + 1]);
sum += (long) (curSample * curSample);
}
return sum;
}
后面的音频帧只要小于最大能量帧*门限值(M),当前短时能量小时,就动态调小门限值,当音量衰减的幅值太大,就定义为非语音帧,启动非语音计数,非语音帧连续计数达200,相当于停顿2秒,则表示讲话结束,若中间有语音帧数据,则计数器复位,重新计数。
private static final int RMS_COUNT_MAX = 200; // 2s
long rms = getRms(this.mRecordedLength, this.mOneSec);
if (rms > this.highestRMS) {
this.highestRMS = rms;
this.rmsCount = 0;
return false;
} else if (((double) rms) < 0.5d * ((double) this.highestRMS)) {
if(this.rmsCount < RMS_COUNT_MAX){
this.rmsCount++;
return false;
}else{
this.rmsCount = 0;
return true;
}
} else {
this.rmsCount = 0;
return false;
}
VAD有效取值可以大大的减少非语音帧的传输和处理,提高了效率。
四、网络问题
网络的差异就很好理解了,不同的网络接入点,不同的时段测试都可能会影响结论的,只有使用同一个接入点,同一时间测试,才能减少网络差异引起的问题。
五、手机差异性
Android系统的开放性也造成了Android手机硬件一致性非常差,从而形成的差异性(兼容性),一个WiFi模块15块能买到,8块也能买到,价格不同,性能当然也会不一样的,所以只能多拿主流手机测试验证。