设置audio单元格式并渲染交错PCMaudio的回叫

我目前正在尝试播放一系列UDP数据包中收到的audio。 这些被解码成具有以下属性的PCM帧:

  • 2个频道
  • 交错
  • 单个通道每个采样2个字节(所以每帧4个字节)
  • 采样率为48000。

每个UDP数据包包含480帧,所以缓冲区的大小是480 * 2(通道)* 2(每通道字节数)。

我需要设置一个audio单元来播放这些数据包。 所以,我的第一个问题是,我应该如何设置audio单元的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; 

其次,设置后,我不知道如何从UDPcallback帧到实际的audio单元渲染function。

我目前有一个来自套接字侦听器的callback函数,在该函数中,我生成了包含要播放的audio的int16 *缓冲区。 据我了解,我也必须实现一个呈现callback为以下forms的audio单元:

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

把它放在一起,我认为我的套接字接收callback应该做的是解码帧,并把它们放在一个缓冲区结构,以便RenderFramescallback可以从该缓冲区获取帧,并播放它们。 它是否正确? 如果是这样,一旦我在RenderFrames函数中获取下一帧, 我怎么实际“提交”回放

一次拿这个部分

AudioStreamBasicDescriptor

苹果的ASBD文件就在这里 。 澄清:

  • 一帧audio是时间重合的一组audio样本。 换句话说,每个通道一个样本。 对于立体声,因此这是2
  • 对于PCM格式,没有分组。 据说, mBytesPerPacket = mBytesPerFramemFramesPerPacket=1但我不确定这是否实际上曾经使用。
  • mReserved不使用,必须为0
  • 请参阅mFormatIDmFormatFlags 的文档 。 CoreAudioTypes.h中有一个方便的辅助函数CalculateLPCMFlags ,用于计算CoreAudioTypes.h中的后者。
  • 多声道audio通常是交错的(如果你真的不想要它,你可以在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; 

呈现callback

CoreAudio运行苹果形容为模型。 也就是说,当CoreAudio需要缓冲区填充时,渲染callback被称为实时线程。 从你的问题看来,你正在期待着相反的 – 把数据推到audio输出。

基本上有两个实施select:

  1. 在呈现callback中从UDP套接字执行非阻塞读取(一般来说,您在这里执行的任何操作都应该是快速且无阻塞的)。
  2. 在渲染callback接收和消耗时保持插入样本的audioFIFO。

第二个可能是更好的select,但是你将需要pipe理自己的缓冲区溢出和溢出。

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

在您的应用程序中,您可能会对解码后的audio数据包进行分散 – 收集,在解码时分配缓冲区。 但是,您并不总是得到您想要的延迟,并且可能无法安排inNumberFrames与您的解码的UDPaudio帧相同。