使用AudioUnit

时间:2015-07-05 19:21:44

标签: ios objective-c cocoa core-audio audiounit

我试图制作一个准确的计时器来分析输入。我希望能够测量~200ms信号的1%偏差。 我的理解是使用AudioUnit将能够获得<1ms。 我尝试从Stefan Popp's example实现代码 在更新了一些事情以使其在xcode 6.3上工作之后,我让这个例子正常工作,但是:

  1. 虽然我最终想要捕获音频,但我认为应该有某种方式来获取通知,比如NSTimer,所以我尝试了一个AudioUnitAddRenderNotify,但它完全按照它应该做的那样 - 即它&#39 ; s绑定到渲染,而不仅仅是一个任意的计时器。是否有某种方法可以在不记录或播放的情况下触发回调?

  2. 当我检查mSampleTime时,我发现切片之间的间隔确实与inNumberFrames - 512匹配 - 这可以达到11.6ms。我看到记录和播放的间隔相同。我需要更多的解决方案。

  3. 我尝试使用kAudioSessionProperty_PreferredHardwareIOBufferDuration,但我找到的所有示例都使用弃用的AudioSessions,所以我尝试转换为AudioUnits:

    Float32 preferredBufferSize = .001; // in seconds
    status = AudioUnitSetProperty(audioUnit, kAudioSessionProperty_PreferredHardwareIOBufferDuration, kAudioUnitScope_Output, kOutputBus, &preferredBufferSize, sizeof(preferredBufferSize));
    

    但是我得到OSStatus -10879,kAudioUnitErr_InvalidProperty。

    然后我尝试了值为128和256的kAudioUnitProperty_MaximumFramesPerSlice,但是inNumberFrames总是512。

    UInt32 maxFrames = 128;
    status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFrames, sizeof(maxFrames));
    

    [编辑] 我试图比较输入的时间(用户对MIDI或麦克风的选择)与应该的时间。具体来说,乐器是在节拍/节拍器之前或之后播放的,是多少?这适用于音乐家,而不是游戏,所以预计会很精确。

    [编辑] 答案似乎对事件重新开始。他们让我准确地看到发生了什么事情,但是我不知道我是如何准确地做某事的。我的错是不清楚。我的应用程序也需要成为节拍器 - 同步播放节拍上的点击并在节拍上闪现一点 - 然后我可以分析用户的动作以比较时间。但如果我不能准确地演奏节拍,那么其余部分就会分崩离析。也许我应该录制音频 - 即使我不想要它 - 只是为了从回调中获取inTimeStamp?

    [编辑] 目前我的节拍器是:

    - (void) setupAudio
    {
        AVAudioPlayer *audioPlayer;
        NSString *path = [NSString stringWithFormat:@"%@/click.mp3", [[NSBundle mainBundle] resourcePath]];
        NSURL *soundUrl = [NSURL fileURLWithPath:path];
        audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
        [audioPlayer prepareToPlay];
    
        CADisplayLink *syncTimer;
        syncTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(syncFired:)];
        syncTimer.frameInterval = 30;
        [syncTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    -(void)syncFired:(CADisplayLink *)displayLink
    {
        [audioPlayer play];
    }   
    

2 个答案:

答案 0 :(得分:1)

您应该使用循环缓冲区,并对您自己计时器上所需帧数匹配的信号进行分析。为此,您需要设置一个渲染回调,然后在循环缓冲区中输入回调中的输入音频。然后设置自己的计时器,它将从缓冲区的尾部拉出并进行分析。这样你就可以每隔0.23秒向缓冲区提供1024帧,你的分析计时器可能会每0.000725秒触发一次并分析32个样本。 Here是关于循环缓冲区的相关问题。

修改

要使用环形缓冲区获得精确计时,您还可以存储与音频缓冲区对应的时间戳。我使用TPCircularBuffer来做到这一点。 TPCircularBufferPrepareEmptyAudioBufferList,TPCircularBufferProduceAudioBufferList和TPCircularBufferNextBufferList将复制和检索环形缓冲区中的音频缓冲区和时间戳。然后,在进行分析时,每个缓冲区都会有一个时间戳,无需在渲染线程中完成所有工作,并允许您选择分析窗口。

答案 1 :(得分:0)

如果您正在使用互相关和/或峰值检测器之类的东西在音频样本缓冲区(或包含样本的环形缓冲区)中找到匹配的样本向量,那么您应该能够在尖锐事件之间对样本进行计数在一个样本内(44.1k Hz采样率下1/44100或0.0226757毫秒),加上或减去一些时间估计误差。对于多个音频单元缓冲区分开的事件,您可以在中间缓冲区内对样本数求和,以获得比仅使用(更粗略)缓冲区时序更精确的时间间隔。

但请注意,每个采样缓冲区和扬声器音频输出之间以及麦克风声音接收和缓冲区回调之间存在延迟或延迟。必须对其进行测量,因为您可以测量发送样本缓冲区之间的往返时间,以及输入缓冲区自相关估计函数何时将其恢复。这是硬件缓冲,转换(模拟到数字,反之亦然)并传递数据所需的时间。使用适当的音频会话设置,延迟可能在5.8到2毫秒的范围内,但对于不同的iOS设备可能会有所不同。

是的,测量音频的最准确方法是捕获音频并查看实际采样音频流中的数据。