设置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 = mBytesPerFrame
,mFramesPerPacket=1
但我不确定这是否实际上曾经使用。 -
mReserved
不使用,必须为0
- 请参阅
mFormatID
和mFormatFlags
的文档 。 CoreAudioTypes.h中有一个方便的辅助函数CalculateLPCMFlags
,用于计算CoreAudioTypes.h中的后者。 - 多声道audio通常是交错的(如果你真的不想要它,你可以在
mFormatFlags
设置一些)。 - 还有一个辅助函数可以填充整个ASBD –
FillOutASBDForLPCM()
以用于线性PCM的常见情况。 - remoteIO单元不支持
mFormatID
和mFormatFlags
许多组合 – 我发现在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:
- 在呈现callback中从UDP套接字执行非阻塞读取(一般来说,您在这里执行的任何操作都应该是快速且无阻塞的)。
- 在渲染callback接收和消耗时保持插入样本的audioFIFO。
第二个可能是更好的select,但是你将需要pipe理自己的缓冲区溢出和溢出。
ioData
参数指向分散聚集控制结构。 在最简单的情况下,它指向一个缓冲区,其中包含所有的帧,但可能包含几个之间有足够的帧来满足inNumberFrames
。 通常情况下,预先分配一个足够大的inNumberFrames
的缓冲区,将样本复制到其中,然后修改指向购买ioData
的AudioBufferList
对象以指向它。
在您的应用程序中,您可能会对解码后的audio数据包进行分散 – 收集,在解码时分配缓冲区。 但是,您并不总是得到您想要的延迟,并且可能无法安排inNumberFrames
与您的解码的UDPaudio帧相同。