【发布时间】:2016-02-15 05:09:48
【问题描述】:
我最近使用 AudioUnits 在 Mac 上设计了一个录音机。它被设计为像一个视频安全系统一样,连续录制,带有用于回放浏览的功率级别的图形显示。 我注意到每 85 分钟失真就会出现 3 分钟。经过一天的排除,似乎在调用回调之前发生的声音采集使用了循环缓冲区,并且回调的 audioUnitRender 函数从该缓冲区中提取但速度稍慢,最终导致内部缓冲区写入回绕并捕获加上 audioUnitRender 读取。双工操作测试显示延迟不断增加,在 85 分钟后,您会听到大约 200-300 毫秒的延迟,并且由于渲染缓冲区帧在缓冲区的末尾和开头具有缓冲区段的组合,即长延迟和短延迟,因此噪音开始了。当指针分开时,噪音消失,您会听到原始短延迟的干净音频,然后在 85 分钟后再次重复。即使使用低影响的回调处理,这仍然会发生。我看过一些关于延迟的帖子,但没有关于冲突的帖子,有人看过吗?
osx 10.9.5,xcode 6.1.1 代码详情:-
//modes 1=playback, 2=record, 3=both
AudioComponentDescription outputcd = {0}; // 10.6 version
outputcd.componentType = kAudioUnitType_Output;
outputcd.componentSubType = kAudioUnitSubType_HALOutput; //allows duplex
outputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
AudioComponent comp = AudioComponentFindNext (NULL, &outputcd);
if (comp == NULL) {printf ("can't get output unit");exit (-1);}
CheckError (AudioComponentInstanceNew(comp, au),"Couldn't open component for outputUnit");
//tell input bus that its's input, tell output it's an output
if(mode==1 || mode==3) r=[self setAudioMode:*au :0];//play
if(mode==2 || mode==3) r=[self setAudioMode:*au :1];//rec
// register render callback
if(mode==1 || mode==3) [self setCallBack:*au :0];
if(mode==2 || mode==3) [self setCallBack:*au :1];
// if(mode==2 || mode==3) [self setAllocBuffer:*au];
// get default stream, change amt of channels
AudioStreamBasicDescription audioFormat;
UInt32 k=sizeof(audioFormat);
r= AudioUnitGetProperty(*au,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
1,
&audioFormat,
&k);
audioFormat.mChannelsPerFrame=1;
r= AudioUnitSetProperty(*au,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
1,
&audioFormat,
k);
//start
CheckError (AudioUnitInitialize(outputUnit),"Couldn't initialize output unit");
//record callback
OSStatus RecProc(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
myView * mv2=(__bridge myView*)inRefCon;
AudioBuffer buffer,buffer2;
OSStatus status;
buffer.mDataByteSize = inNumberFrames *4 ;// buffer size
buffer.mNumberChannels = 1; // one channel
buffer.mData =mv2->rdata;
buffer2.mDataByteSize = inNumberFrames *4 ;// buffer size
buffer2.mNumberChannels = 1; // one channel
buffer2.mData =mv2->rdata2;
AudioBufferList bufferList;
bufferList.mNumberBuffers = 2;
bufferList.mBuffers[0] = buffer;
bufferList.mBuffers[1] = buffer2;
status = AudioUnitRender(mv2->outputUnit, ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, &bufferList);
[mv2 recproc :mv->rdata :mv->rdata2 :inNumberFrames];
return noErr;
}
【问题讨论】:
-
故障原因或错误可能在您的采样率设置中、在您的音频单元回调函数中或在您未显示的文件编写代码中。
-
我想我已经消除了所有这些。我的第一个想法是这是我的 adpcm 压缩例程,但我很幸运地看到了这个问题(等待 85 分钟需要很大的耐心),它是按比例缩小的版本,没有压缩,没有存档,只是播放录制的内容(带耳机)。还要注意延迟的变化,当问题发生时,您会听到延长的延迟以及大约 20 毫秒延迟的原始副本。然后长延迟复制和失真消失,音频很干净,直到 85 分钟后再次发生。
-
继续:- 这最后的观察暗示它是一个内部循环缓冲区问题。我会每隔一段时间扔掉几帧以避免这种情况,这是我知道的。每个回调可能会错过一个 audiounitrender 调用(或添加一个额外的调用),但我不确定。希望有人知道内部隐藏有关此的场景细节因为我相信它在音频硬件驱动程序(kext)中
-
我也在使用默认音频描述,因为我使用了 audiounitgetproperty 它是什么,mod 只是通道数(想要单声道),然后将其设置回来,我遇到的唯一问题是我必须匹配 audiounitrender缓冲区参数匹配(或得到错误-50)。所以一切都是干净的 1 1/2 小时,然后噼啪作响 3 分钟,然后消失,只在 1 1/2 小时内再次回来。顺便说一句,我的代码不使用任何循环缓冲区,只是设计了两倍大的直线缓冲区,我知道我需要安全。
标签: audio core-audio latency audiounit circular-buffer