语音识别的客户端侧实现-音频实时上传
上篇文章讲了如何录音和编码压缩,随后的步骤就是上传了。上传的过程很简单,使用通用的网络框架上传即可。
这一篇我们讲一下,如何边录音边上传,边识别。框架图如下:
对于客户端来说整个流程相对简单,其中比较复杂的就是各种容错的处理。
1.缓冲区上传并不是每次都成功,所我们在上传数据之前,还需要将音频数据写到本地中,并做好标记,如果因为各种原因上传失败,就需要重传的过程。
2.服务端每次需要根据此次上传的音频和此前上传的音频进行拼接然后做一个整体的识别,以防止分段识别中产生的错误。这就需要服务端在每次给服务端返回数据的时候,对以前的数据进行纠错,并在最后一次上传后,整体返回所有的数据。
为了能获取缓冲区的数据,我们必须使用上面文章提到的比较底层的C语言的API来实现整个的录制过程,这也是为什么虽然他使用的很多接口一倍废弃,我们仍然使用它的原因。希望苹果以后可以提供一个新的示例代码来实现整个流程,不过一般来说对于这种底层的录制需求可能比较少,可能我们只能一直使用speakhere的例子中的代码了。
// AudioQueuecallback function, called when an input buffers has been filled.
voidAQRecorder::MyInputBufferHandler(void *inUserData,AudioQueueRefinAQ,
AudioQueueBufferRefinBuffer,constAudioTimeStamp *inStartTime,UInt32inNumPackets,
constAudioStreamPacketDescription*inPacketDesc)
{
AQRecorder *aqr = (AQRecorder*)inUserData;
try {
if (inNumPackets > 0) {
// write packets tofile
XThrowIfError(AudioFileWritePackets(aqr->mRecordFile,FALSE, inBuffer->mAudioDataByteSize,
inPacketDesc, aqr->mRecordPacket,&inNumPackets, inBuffer->mAudioData),
"AudioFileWritePackets failed");
aqr->mRecordPacket+= inNumPackets;
}
// if we're not stopping,re-enqueue the buffe so that it gets filled again
if (aqr->IsRunning())
XThrowIfError(AudioQueueEnqueueBuffer(inAQ, inBuffer, 0,NULL), "AudioQueueEnqueueBuffer failed");
} catch (CAXException e) {
char buf[256];
fprintf(stderr, "Error:%s (%s)\n", e.mOperation, e.FormatError(buf));
}
}
具体的实现中,我们需要在这个回调中,我们除了调用AudioFileWritePackets还需要自己写相关的上传音频的代码。
再上传过程中,除了这些原始的pcm数据外还需要和服务端协商定义一些头,以便我们后来的重传。如果做得更加深入的话,我们还需要做一个网络请求队列,因为有时候在某些特殊的网络情况下,我们需要做并发的上传操作。具体的实现方式和代码就不再详细讲了。