1. 程式人生 > >【iOS筆記】AudioUnit錄音異常(聽起來類似於丟幀丟資料)

【iOS筆記】AudioUnit錄音異常(聽起來類似於丟幀丟資料)

在做語音識別專案時候發現一個問題,識別率奇低無比……所以就把原始音訊資料錄下來,發現音訊丟資料。

實驗機器:iPhone6s iOS12

問題程式碼如下:

OSStatus AudioUnitInput::RecordCallback(
    void *inRefCon,
    AudioUnitRenderActionFlags *ioActionFlags,
    const AudioTimeStamp *inTimeStamp,
    UInt32 inBusNumber,
    UInt32 inNumberFrames,
    AudioBufferList *ioData)
{
  int count = 0;
  OSStatus status = noErr;

  if (inNumberFrames > 0) {
    AudioUnitInput *ars = (AudioUnitInput *)inRefCon;
    status = AudioUnitRender(ars->mRecordUnit,
                             ioActionFlags,
                             inTimeStamp,
                             inBusNumber,
                             inNumberFrames,
                             ars->recordBufList);
  } else {
    NSLog(@"inNumberFrames is %d", inNumberFrames);
  }
  return noErr;
}
…………
…………
…………
bufferByteSize = ComputeRecordBufferSize(&mRecordFormat,
                                             kBufferDurationSeconds);
    NSLog(@"Compute recorder %f seconds data buffer size is %d.",
        kBufferDurationSeconds,
        bufferByteSize);
    recordBufList = (AudioBufferList *)malloc(sizeof(AudioBufferList));
    recordBufList->mNumberBuffers = 1;
    recordBufList->mBuffers[0].mNumberChannels = 1;
    recordBufList->mBuffers[0].mDataByteSize = bufferByteSize * 2;
    recordBufList->mBuffers[0].mData = malloc(bufferByteSize * 2);​

以上程式碼可以看出,AudioBufferList我是在初始化的時候進行了malloc,然後在recordCallback中進行資料獲取。理論上這樣是沒問題的,很多教程這麼寫。mData的malloc大小是通過計算的,bufferByteSize是20ms 16k 16bit的大小,即640。但是我發現recordCallback中frame數為341,即682位元組,所以我就malloc了640的兩倍。這裡還有一個很好玩的點,就是如果我把playout開起來進行echo,在playCallback進行資料儲存,這樣獲取的資料就非常正常。

以下給出兩種解決方法:

1. 把malloc放在recordCallback中進行

OSStatus AudioUnitInput::RecordCallback(
    void *inRefCon,
    AudioUnitRenderActionFlags *ioActionFlags,
    const AudioTimeStamp *inTimeStamp,
    UInt32 inBusNumber,
    UInt32 inNumberFrames,
    AudioBufferList *ioData)
{
  int count = 0;
  OSStatus status = noErr;
  UInt16 numSamples = inNumberFrames*1;

  if (inNumberFrames > 0) {
    AudioUnitInput *ars = (AudioUnitInput *)inRefCon;
    ars->recordBufList = (AudioBufferList *)malloc(sizeof(AudioBufferList));
    ars->recordBufList->mNumberBuffers = 1;
    ars->recordBufList->mBuffers[0].mNumberChannels = 1;
    ars->recordBufList->mBuffers[0].mDataByteSize = numSamples * sizeof(UInt16);
    ars->recordBufList->mBuffers[0].mData = malloc(numSamples * sizeof(UInt16));
    status = AudioUnitRender(ars->mRecordUnit,
                             ioActionFlags,
                             inTimeStamp,
                             inBusNumber,
                             inNumberFrames,
                             ars->recordBufList);
  } else {
    NSLog(@"inNumberFrames is %d", inNumberFrames);
  }
  return noErr;
}

2.還有一種是不用malloc了,也是在recordCallback中分配一個AudioBufferList

OSStatus AudioUnitInput::RecordCallback(
    void *inRefCon,
    AudioUnitRenderActionFlags *ioActionFlags,
    const AudioTimeStamp *inTimeStamp,
    UInt32 inBusNumber,
    UInt32 inNumberFrames,
    AudioBufferList *ioData)
{
  int count = 0;
  OSStatus status = noErr;
  AudioBufferList bufferList;
  UInt16 numSamples = inNumberFrames*1;
  UInt16 samples[numSamples];
  memset(&samples, 0, sizeof(samples));
  bufferList.mNumberBuffers = 1;
  bufferList.mBuffers[0].mData = samples;
  bufferList.mBuffers[0].mNumberChannels = 1;
  bufferList.mBuffers[0].mDataByteSize = numSamples * sizeof(UInt16);

  if (inNumberFrames > 0) {
    AudioUnitInput *ars = (AudioUnitInput *)inRefCon;
    status = AudioUnitRender(ars->mRecordUnit,
                             ioActionFlags,
                             inTimeStamp,
                             inBusNumber,
                             inNumberFrames,
                             &bufferList);
  } else {
    NSLog(@"inNumberFrames is %d", inNumberFrames);
  }
  return noErr;
}

具體為什麼還不是很清楚,難道是堆疊的問題?

感謝