3

我目前正在使用 TPCircularBuffer 来同步解码来自外部库(libxmp:http://xmp.sourceforge.net/)的音频数据,通过 OS X 上的 AudioUnits API 播放它。

Mach 信号量用于在需要重新填充缓冲区时发出信号。

但是,当信号量被触发时,音频中似乎存在“间隙”(并且音频似乎播放得比平时慢)。

在这种情况下,是否可以使用任何延迟较低的同步方法?

概念验证在这里: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;
 }
4

1 回答 1

2

似乎 Apple DTS 建议不要做任何可以在短的实时音频单元渲染回调中锁定或分配内存的事情,甚至可能包括信号量和马赫信号。

相反,应用程序可以在另一个线程(而不是渲染回调线程)中重复轮询无锁循环 fifo。鉴于 fifo 的采样率和大小都是已知的,一个明显快于 fifo 空到阈值率的轮询率应该可以工作,相当有效,并且不需要锁。延迟可以通过改变阈值级别和相应的轮询速率来控制。重复的 NSTimer 或 CADisplayLink(或 Open GL 帧渲染)计时器可能适用于轮询。

请注意,循环缓冲区需要大于填充阈值,以便填充例程有足够的时间与音频回调异步工作。当然,最坏情况下的填充率必须比最佳情况下的 fifo 清空率更快。

于 2014-05-18T00:46:33.453 回答