代码之家  ›  专栏  ›  技术社区  ›  Pierre P.

输出流格式被压缩的AUHAL单元的AudioBufferList。

  •  2
  • Pierre P.  · 技术社区  · 7 年前

    确认书

    我知道这篇文章很长,但我尽量将我的问题背景化,因为我认为它很独特(除了 this one . 最后一个问题在文章的最后 here's the complete code .

    首先,一点背景知识。我正在使用 CoreAudio公司 音频工具箱 更准确地说,是库 音频单元 . 我在macOS上。我的最终目标是从任何输入设备记录音频(因此在简单的AudioQueueBuffer上使用音频单元),并将其写入音频文件。我认为我的程序最棘手的部分是在 单个音频单元 ,因此没有使用螺旋描记器。

    我的程序基本上只是一个音频单元,封装在一个类中, AudioUnit mInputUnit 这是一个奥哈尔单位。因此,我遵循了这一点 this technical note 设置它。基本上,我将输入元素的输入范围(因为输出元素已禁用)链接到音频设备,即我的内置麦克风。

    然后,我相应地更新单元输出范围的音频格式。

      ...
      inputStream.mFormatID = kAudioFormatMPEG4AAC;
      inputStream.mFormatFlags = 0;
      inputStream.mBitsPerChannel = 0;
      checkError(
        AudioUnitSetProperty(
          mInputUnit,
          kAudioUnitProperty_StreamFormat,
          kAudioUnitScope_Output,
          1,
          &inputStream,
          propertySize
        ),
        "Couldn't set output stream format."
      );
    

    因此,此时,音频单元 应该 工作如下:

    LPCM[输入范围]中输入设备的记录==>从LPCM转换为==>在AAC中渲染。

    请注意,每个流格式(输入和输出)使用 2个通道 . 输入流和输出流都没有 mFormatFlags 设置为 kAudioFormatIsNonInterleaved ,因此它们都是交错的。 事实上,我认为这就是问题的根源,但不明白为什么。

    在这一点上,一切似乎都正常。在设置输入回调后,当我尝试渲染音频单元时,会出现问题。

    我发现了一张便条,上面写着:

    按照惯例,AUHAL解交织多声道音频。这意味着您设置了两个每个通道的音频缓冲区,而不是设置一个mNumberChannels==2的音频缓冲区。AudioUnitRender()调用中参数r(-50)问题的一个常见原因是audiobufferlist的拓扑(或缓冲区的排列)与单元准备生成的内容不匹配。当在单位级别进行交易时,您几乎总是希望这样做。

    摘自:克里斯·亚当森;凯文·阿维拉。–Learning Core Audio:Mac和iOS音频编程实践指南。–iBooks。

    因此,我遵循了适当的代码结构来呈现音频。

    OSStatus Recorder::inputProc(
      void *inRefCon,
      AudioUnitRenderActionFlags *ioActionFlags,
      const AudioTimeStamp *inTimeStamp,
      UInt32 inBusNumber,
      UInt32 inNumberFrames,
      AudioBufferList *ioData
    )
    {
      Recorder *This = (Recorder *) inRefCon;
      CAStreamBasicDescription outputStream;
      This->getStreamBasicDescription(kAudioUnitScope_Output, outputStream);
    
      UInt32 bufferSizeBytes = inNumberFrames * sizeof(Float32);
      UInt32 propertySize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * outputStream.mChannelsPerFrame);
      auto bufferList = (AudioBufferList*) malloc(propertySize);
      bufferList->mNumberBuffers = outputStream.mChannelsPerFrame;
    
      for(UInt32 i = 0; i < bufferList->mNumberBuffers; ++i)
      {
        bufferList->mBuffers[i].mNumberChannels = 1;
        bufferList->mBuffers[i].mDataByteSize = bufferSizeBytes;
        bufferList->mBuffers[i].mData = malloc(bufferSizeBytes);
      }
    
      checkError(
        AudioUnitRender(
          This->mInputUnit,
          ioActionFlags,
          inTimeStamp,
          inBusNumber,
          inNumberFrames,
          bufferList
        ),
        "Couldn't render audio unit."
      );
      free(bufferList);
    }
    

    然后,当我尝试渲染音频时,出现以下错误 Error: Couldn't render audio unit. (-50) 这实际上是一个应该 固定的 跟着笔记走,这让我更加困惑。

    问题是

    此时,我不知道这是否与我的总体架构有关,即我是否应该使用 螺旋记录仪 并添加一个输出单元,而不是在单个AUHAL单元内尝试从规范格式转换为压缩格式? 或者这与我预先分配AudioBufferList的方式有关?

    1 回复  |  直到 7 年前
        1
  •  1
  •   Pierre P.    7 年前

    通过重新设计整个流程,我成功地解决了这个问题。简而言之,我仍然有一个唯一的AUHAL单元,但我没有在AUHAL单元内进行格式转换,而是在渲染回调中使用扩展音频文件进行转换,该文件采用源格式和目标格式。 整个挑战是找到正确的格式描述,这基本上只是测试不同的值 mFormatID , mFormatFlags