AudioQueue 吃了我的缓冲区(前 15 毫秒)
Posted
技术标签:
【中文标题】AudioQueue 吃了我的缓冲区(前 15 毫秒)【英文标题】:AudioQueue ate my buffer (first 15 milliseconds of it) 【发布时间】:2010-04-02 03:11:04 【问题描述】:我正在以编程方式生成音频。我听到缓冲区之间的沉默间隙。当我将手机连接到示波器时,我发现每个缓冲区的前几个样本都丢失了,取而代之的是静默。这种静默的长度从几乎没有变化到长达 20 毫秒。
我的第一个想法是我原来的回调函数花费了太多时间。我用最短的可能替换它——它一遍又一遍地重新排列相同的缓冲区。我观察到同样的行为。
AudioQueueRef aq;
AudioQueueBufferRef aq_buffer;
AudiostreamBasicDescription asbd;
void aq_callback (void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
OSStatus s = AudioQueueEnqueueBuffer(aq, aq_buffer, 0, NULL);
void aq_init(void)
OSStatus s;
asbd.mSampleRate = AUDIO_SAMPLES_PER_S;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
asbd.mBytesPerPacket = 1;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerFrame = 1;
asbd.mChannelsPerFrame = 1;
asbd.mBitsPerChannel = 8;
asbd.mReserved = 0;
int PPM_PACKETS_PER_SECOND = 50;
// one buffer is as long as one PPM frame
int BUFFER_SIZE_BYTES = asbd.mSampleRate/PPM_PACKETS_PER_SECOND*asbd.mBytesPerFrame;
s = AudioQueueNewOutput(&asbd, aq_callback, NULL, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &aq);
s = AudioQueueAllocateBuffer(aq, BUFFER_SIZE_BYTES, &aq_buffer);
// put samples in the buffer
buffer_data(my_data, aq_buffer);
s = AudioQueueStart(aq, NULL);
s = AudioQueueEnqueueBuffer(aq, aq_buffer, 0, NULL);
【问题讨论】:
【参考方案1】:我不熟悉 iPhone 音频 API,但它似乎类似于其他通常您会排队多个缓冲区的 API,这样当系统完成处理第一个缓冲区时,它可以立即开始处理正在执行第一个缓冲区的完成回调时,下一个缓冲区(因为它已经排队)。
类似:
AudioQueueRef aq;
AudioQueueBufferRef aq_buffer[2];
AudioStreamBasicDescription asbd;
void aq_callback (void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
// note that the callback tells us which buffer has been completed, so all
// we have to do is queue it back up
OSStatus s = AudioQueueEnqueueBuffer(aq, inBuffer, 0, NULL);
void aq_init(void)
OSStatus s;
asbd.mSampleRate = AUDIO_SAMPLES_PER_S;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
asbd.mBytesPerPacket = 1;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerFrame = 1;
asbd.mChannelsPerFrame = 1;
asbd.mBitsPerChannel = 8;
asbd.mReserved = 0;
int PPM_PACKETS_PER_SECOND = 50;
// one buffer is as long as one PPM frame
int BUFFER_SIZE_BYTES = asbd.mSampleRate/PPM_PACKETS_PER_SECOND*asbd.mBytesPerFrame;
s = AudioQueueNewOutput(&asbd, aq_callback, NULL, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &aq);
s = AudioQueueAllocateBuffer(aq, BUFFER_SIZE_BYTES, &aq_buffer[0]);
s = AudioQueueAllocateBuffer(aq, BUFFER_SIZE_BYTES, &aq_buffer[1]);
// put samples in the buffer - fill both buffers
buffer_data(my_data, aq_buffer[0]);
buffer_data(my_data, aq_buffer[1]);
s = AudioQueueStart(aq, NULL);
s = AudioQueueEnqueueBuffer(aq, aq_buffer[0], 0, NULL);
s = AudioQueueEnqueueBuffer(aq, aq_buffer[1], 0, NULL);
【讨论】:
感谢您指出我可以在 init() 中将多个缓冲区排入队列——这个重要的事实让我无法理解。实施此建议以及对回调的更改改善了我的情况,但并没有解决它。我仍然想念一些缓冲区的前几毫秒。现在这种情况仅每 7 或 8 个数据包发生一次,我错过的样本更少,但仍然很明显。 在这个简单的例子中,这似乎并不重要,但以防万一:超过 2 个缓冲区是否有帮助(如果你还没有尝试过)?此外,您确定buffer_data()
正在用整整 1/50 秒的数据包填充缓冲区吗? AUDIO_SAMPLES_PER_S
的值是多少?
感谢您一直以来的支持。如果我使用 3 个缓冲区而不是 2 个缓冲区,我就不会丢失样本。这看起来很神奇……这种魔法让我感到不舒服,因为我无法解释。我想知道是否有某种双缓冲正在进行。我以前尝试过使用缓冲区的大小,使它们长达 1 秒 (PPM_PACKETS_PER_SECOND = 1
)。使用 2 个长缓冲区,我仍然会丢失样本。使用 3 个短缓冲区,我可以取回我放入的所有内容。
为了完整起见,您的其他问题的答案:我用定义明确的值填充缓冲区的末端,直到它们的长度(buffer_data 检查AudioQueueBuffer.mAudioDataBytesCapacity
)和AUDIO_SAMPLES_PER_S
是#define
' d 到 44100。以上是关于AudioQueue 吃了我的缓冲区(前 15 毫秒)的主要内容,如果未能解决你的问题,请参考以下文章