设置音频单元格式并渲染交错PCM音频的回调

Ser*_*les 4 audio audiounit ios

我正在尝试播放我在一系列UDP数据包中收到的音频.它们被解码为具有以下属性的PCM帧:

  • 2个频道
  • 交错
  • 单个通道中每个样本2个字节(每帧4个字节)
  • 采样率为48000.

每个UDP数据包包含480个帧,因此缓冲区的大小为480*2(通道)*2(每个通道的字节数).

我需要设置一个音频单元来播放这些数据包.所以,我的第一个问题是,我应该如何为音频单元设置AudioStreamBasicDescription结构?查看文档我甚至不确定交错PCM是否是可接受的格式.

这是我到目前为止所得到的:

struct AudioStreamBasicDescription {
   Float64 mSampleRate;                 //48000
   UInt32  mFormatID;                   //?????
   UInt32  mFormatFlags;                //?????
   UInt32  mBytesPerPacket;             //Not sure what "packet" means here
   UInt32  mFramesPerPacket;            //Same as above
   UInt32  mBytesPerFrame;              //Same
   UInt32  mChannelsPerFrame;           //2?
   UInt32  mBitsPerChannel;             //16?
   UInt32  mReserved;                   //???
};
typedef struct AudioStreamBasicDescription  AudioStreamBasicDescription;
Run Code Online (Sandbox Code Playgroud)

其次,在设置之后,我不确定如何将帧从UDP回调到实际的音频单元渲染功能.

我目前有一个来自套接字监听器的回调函数,我在其中生成包含我想要播放的音频的int16*缓冲区.据我了解,我还必须为以下形式的音频单元实现渲染回调:

OSStatus RenderFrames(
    void                        *inRefCon,
    AudioUnitRenderActionFlags  *ioActionFlags,
    const AudioTimeStamp        *inTimeStamp,
    UInt32                      inBusNumber,
    UInt32                      inNumberFrames,
    AudioBufferList             *ioData)
{
    //No idea what I should do here.
    return noErr;
}
Run Code Online (Sandbox Code Playgroud)

总而言之,我认为我的套接字接收回调应该做的是解码帧,并将它们放在缓冲结构中,以便RenderFrames回调可以从该缓冲区中获取帧,并播放它们.它是否正确?如果是,一旦我在RenderFrames函数中获取下一帧,我如何实际"提交它"进行播放

mar*_*rko 6

一次拿这个部分

AudioStreamBasicDescriptor

Apple的ASBD文档就在这里.澄清:

  • 音频帧是一组时间重合的音频样本.换句话说,每个通道一个样本.因此,对于立体声而言2.
  • 对于PCM格式,没有分组.据称,mBytesPerPacket = mBytesPerFrame,mFramesPerPacket=1但我不知道这是否是真正被使用.
  • mReserved 没有使用,必须 0
  • 请参阅该文档mFormatIDmFormatFlags.CalculateLPCMFlagsCoreAudioTypes.h中有一个方便的辅助函数,用于计算后者CoreAudioTypes.h.
  • 多声道音频通常是交错的(mFormatFlags如果你真的不想要它,你可以设置一下).
  • 还有另一个辅助函数可以填充整个ASBD - FillOutASBDForLPCM()适用于线性PCM的常见情况.
  • remoteIO单元的许多组合mFormatIDmFormatFlags不支持 - 我发现在iOS上需要进行实验.

这是我的一个项目的一些工作代码:

AudioStreamBasicDescription inputASBL = {0}; 

inputASBL.mSampleRate =          static_cast<Float64>(sampleRate);
inputASBL.mFormatID =            kAudioFormatLinearPCM;
inputASBL.mFormatFlags =         kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger,
inputASBL.mFramesPerPacket =     1;
inputASBL.mChannelsPerFrame =    2;
inputASBL.mBitsPerChannel =      sizeof(short) * 8;
inputASBL.mBytesPerPacket =      sizeof(short) * 2;
inputASBL.mBytesPerFrame =       sizeof(short) * 2;
inputASBL.mReserved =            0;
Run Code Online (Sandbox Code Playgroud)

渲染回调

CoreAudio运行Apple所说的模型.也就是说,当CoreAudio需要缓冲区填充时,渲染回调被称为实时线程.根据您的问题,您似乎期待相反的情况 - 将数据推送到音频输出.

基本上有两种实现选择:

  1. 在渲染回调中从UDP套接字执行非阻塞读取(作为一般规则,您在此处执行的任何操作都应该是快速且无阻塞的).
  2. 保持音频FIFO,当渲染回调接收和使用时,将插入样本.

第二种可能是更好的选择,但是您需要自己管理缓冲区过度和欠量运行.

ioData论点指向分散 - 聚集控制结构.在最简单的情况下,它指向一个包含所有帧的缓冲区,但可以包含几个它们之间有足够的帧来满足inNumberFrames.通常,预先分配一个足够大的缓冲区inNumberFrames,将样本复制到其中,然后修改AudioBufferList指向buy 的对象ioData以指向它.

在您的应用程序中,您可能会对解码后的音频数据包采用分散 - 聚集方法,在解码时分配缓冲区.但是,您并不总是得到所需的延迟,并且可能无法安排inNumberFrames与解码的UDP音频帧相同.