OS X 上音频编程的任何低延迟(比马赫信号量)同步方法?
Posted
技术标签:
【中文标题】OS X 上音频编程的任何低延迟(比马赫信号量)同步方法?【英文标题】:Any lower-latency (than Mach semaphores) means of synchronization for audio programming on OS X? 【发布时间】:2014-05-16 06:07:46 【问题描述】:我目前正在使用 TPCircularBuffer 来同步解码来自外部库 (libxmp: http://xmp.sourceforge.net/) 的音频数据,并通过 OS X 上的 AudioUnits API 播放它。
马赫信号量用于在缓冲区需要重新填充时发出信号。
但是,当信号量被触发时,音频中似乎存在“间隙”(并且音频似乎播放得比平时慢)。
在这种情况下可以使用任何延迟较低的同步方法吗?
这里有一个概念验证:https://gist.github.com/douglas-carmichael/cda1117e42e917397ed7
这是我传递给回调的结构:
struct StreamData
TPCircularBuffer ringBuffer;
semaphore_t semaphore;
int fillThreshold;
;
这是我创建信号量的方式:
// Initialize our semaphore
mach_port_t self = mach_task_self();
kern_return_t ret = semaphore_create(self, &ourStream.semaphore, SYNC_POLICY_FIFO, 0);
if (ret != KERN_SUCCESS)
NSLog(@"Semaphore creation failed. Error <%d, %s>", ret, mach_error_string(ret));
return 0;
这是播放循环:
// Start our playback loop
struct xmp_frame_info ourFrameInfo;
int err = true;
while (xmp_play_frame(myContext) == 0)
xmp_get_frame_info(myContext, &ourFrameInfo);
if (ourFrameInfo.loop_count > 0)
break;
/* Are we getting a buffer overrun? */
if (err != false)
err = TPCircularBufferProduceBytes(&ourStream.ringBuffer, ourFrameInfo.buffer, ourFrameInfo.buffer_size);
semaphore_wait(ourStream.semaphore);
这里是渲染回调:
static OSStatus renderModuleCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inBufferFrames,
AudioBufferList *ioData)
struct StreamData *ourStream = inRefCon;
/* Initialize our variable for how much is available */
int bytesAvailable = 0;
/* Grab the data from the circular buffer into a temporary buffer */
SInt16 *ourBuffer = TPCircularBufferTail(&ourStream->ringBuffer, &bytesAvailable);
/* Do we have enough data? */
/* Note: fillThreshold is the maximum output buffer size for the device. */
if (bytesAvailable < ourStream->fillThreshold)
semaphore_signal(ourStream->semaphore);
/* memcpy() the data to the audio output */
memcpy(ioData->mBuffers[0].mData, ourBuffer, bytesAvailable);
/* Clear that section of the buffer */
TPCircularBufferConsume(inRefCon, bytesAvailable);
return noErr;
【问题讨论】:
需要更多细节和一些代码才能在这里得到答案。 我在问题中添加了一个代码示例,完整的概念证明在 gist 链接中。 【参考方案1】:似乎 Apple DTS 建议不要在短的实时音频单元渲染回调中执行任何可能锁定或分配内存的操作,甚至可能包括信号量和马赫信号。
相反,应用可以在另一个线程(而不是渲染回调线程)中重复轮询无锁循环 fifo。鉴于 fifo 的采样率和大小都是已知的,一个明显快于 fifo 空到阈值率的轮询率应该可以工作,相当有效,并且不需要锁。延迟可以通过改变阈值级别和相应的轮询速率来控制。重复的 NSTimer 或 CADisplayLink(或 Open GL 帧渲染)计时器可能适用于轮询。
请注意,循环缓冲区需要大于填充阈值,以便填充例程有足够的时间与音频回调异步工作。而且,当然,最差情况下的填充率必须比最佳情况下的 fifo 清空率更快。
【讨论】:
哪里有使用循环FIFO的例子? TPCircularBuffer 已经是一个无锁循环 FIFO。只需从重复的计时器目标轮询它的状态,以查看它是否有足够的空间容纳更多数据。 类似 NSTimer 的重复计时器?以上是关于OS X 上音频编程的任何低延迟(比马赫信号量)同步方法?的主要内容,如果未能解决你的问题,请参考以下文章
OS X 10.11 El Capitan 上的蓝牙低功耗延迟/延迟