我正在使用ioUnit的渲染回调将音频数据存储到循环缓冲区中:
OSStatus ioUnitRenderCallback(
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
OSStatus err = noErr;
AMNAudioController *This = (__bridge AMNAudioController*)inRefCon;
err = AudioUnitRender(This.encoderMixerNode->unit,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
ioData);
// Copy the audio to the encoder buffer
TPCircularBufferCopyAudioBufferList(&(This->encoderBuffer), ioData, inTimeStamp, kTPCircularBufferCopyAll, NULL);
return err;
}
然后我想从循环缓冲区中读取字节,将它们提供给libLame,然后提供给libShout。 我已经尝试启动一个线程并使用NSCondition让它等到数据可用但由于在Core Audio回调上使用锁定而导致各种问题。
建议的方法是什么?
提前致谢。
我最终接受了亚当的建议并实施了它。
生产者
我在Core Audio Render回调中使用TPCircularBufferProduceBytes将字节添加到循环缓冲区。在我的情况下,我有非交错的音频数据,所以我最终使用了两个循环缓冲区。
消费
答案 0 :(得分:3)
使用重复计时器(NSTimer或CADisplayLink)轮询无锁循环缓冲区或FIFO。如果缓冲区中没有足够的数据,则跳过工作,然后返回(运行循环)。这是因为您知道高精度的采样率,以及您希望或一次需要处理多少数据,因此可以将轮询速率设置得稍快一些,为安全起见,但仍然非常接近与使用条件锁相同的效率。
不建议在实时音频线程回调中使用信号量或锁(或任何其他具有不可预测延迟的内容)。
答案 1 :(得分:2)
你走在正确的轨道上,但你不需要NSCondition。你绝对不想阻止。您正在使用的循环缓冲区实现是无锁的,应该可以解决问题。在音频渲染回调中,通过调用TPCircularBufferProduceBytes
将数据放入缓冲区。然后在阅读器上下文(计时器回调是好的,如hotpaw建议的那样),调用TPCircularBufferTail
来获取尾指针(读取地址)和要读取的可用字节数,然后调用TPCircularBufferConsume
来做实际阅读。现在你已经完成了转移而没有采取任何锁定。只要确保你分配的缓冲区足够大,可以处理由于某种原因你的读取器线程被操作系统阻止的最坏情况,否则你可能会遇到缓冲区溢出情况并丢失数据。