【问题标题】:AudioUnit (Mac) AudioUnitRender internal buffer clashAudioUnit (Mac) AudioUnitRender 内部缓冲区冲突
【发布时间】: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


【解决方案1】:

您似乎正在使用 HAL 输出单元来拉取输入。可能无法保证输入设备和输出设备的采样率完全锁定。任一设备的采样率的任何缓慢轻微漂移最终都可能导致缓冲区下溢或溢出。

一种解决方案可能是为单独的输入音频单元查找并设置输入设备,而不是依赖于默认输出单元。例如,试试 USB 麦克风。

【讨论】:

  • 这是一个非常合乎逻辑的可能性,如果rec回调复制到本地缓冲区然后play回调使用它,两个速率会随着漂移而引起干扰,但它总是会分开更长的时间。它延迟重置意味着必须涉及循环缓冲区,并且总线 1(输入)的记录缓冲区就绪回调触发器比同一总线的实际缓冲区填充事件慢。顺便说一句,该设备是USB声卡。为了消除回调重新进入的可能性,我确实设置了双缓冲,但没有任何改变。
  • 我再次观察到它是一个内部 fifo 循环缓冲区,在输入总线上具有不同的写入/读取时序。最近在播放电影时录制的麦克风音频,当播放部分存档音频时,它开始在某个时间点 3 分钟内听起来刺耳,在此期间,音频有大约 1/4 秒的重复回声。我只使用线性缓冲区,因此回声必须来自内部循环缓冲区。归档 intimestam->msampletime 和日期时间不会在噪音发生时显示上溢/下溢的迹象,只会在每 3 分钟一次昂贵的硬盘写入期间错过回调。
【解决方案2】:

根据这篇文章https://www.native-instruments.com/forum/threads/latency-drift-problem-on-macbook.175551/ 这个问题似乎是maverick 中的USB 音频驱动程序错误。我在任何地方都没有找到 kext 替代解决方案。

制作声纳型测试仪后(每 600 毫秒对扬声器单击 1 个周期 22khz 方波,单击后显示所选记录的帧数),可以看到每秒 3 到 4 个样本漂移以及失真/延迟漂移重置体验1.5 小时后,我决定环顾四周,寻找如何访问缓冲区指针以稳定延迟漂移,但也没有运气。

此外,API 延迟查询在漂移时不会显示任何变化。

我确实发现您可以使用 audiounitstop 然后使用 audiounitstart (相同的线程)重置延迟,但它仅在系统范围内只有一个 audiounit 总线处于活动状态时才有效。研究还表明,如果您在 Audio Midi 设置中切换硬件设备采样率,延迟可能会被重置。这有点激进,对某些人来说会不舒服。

我的设计每 60 分钟切换一次标称采样率(AudioObjectSetPropertyData 和 kAudioDevicePropertyNominalSampleRate)(48000 然后回到 44100),延迟通过回调等待更改通知。

这会导致每小时音频输入和输出出现 2 秒的空白。 Safari 播放 youtube 视频会静音,并在此期间导致 1-2 秒的视频冻结。 VLC 显示相同,但​​视频在 2 秒静音期间保持流畅。

就像我说的那样,它并不适用于所有人,但我选择了系统范围内每小时 2 秒的静音,而不是每 1.5 小时有 3 分钟模糊音频的录音。已经发布了优胜美地升级解决了这个问题,尽管有些人在上优胜美地后也发现噼啪作响。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-02
    • 2015-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多