影响语音交互响应快慢的因素(Android)

语音交互的响应快慢直接影响用户体验,用户希望的是又快又准,即语音识别要准确及响应要快速。语音交互的整个过程:用户唤醒语音交互->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块也能买到,价格不同,性能当然也会不一样的,所以只能多拿主流手机测试验证。

总结:调试最优参数是一个枯燥的过程,要有耐心不停的去偿试,有时候要考虑平衡因素,使结果接近最优值。