代码之家  ›  专栏  ›  技术社区  ›  andyvn22

音频播放的准确(音乐级)计时[关闭]

  •  4
  • andyvn22  · 技术社区  · 14 年前

    我正在尝试编写一个应用程序,它可以安排定时播放一些(大部分是很小的)音频文件。渔获量: 它需要准确的计时,配得上音乐。 . 声音不会播放 频繁地 (例如,它不是取样器或滚筒套件),但它们 需要绝对精确地放置。这个问题分为两部分:

    1. 我应该如何安排时间?我听说过 NSTimer 不准确,或者至少不可靠?我应该用midi吗?或者是一个紧密的循环和一些时间获得高分辨率的功能?

    2. 一旦我有了时间安排,我知道什么时候播放我的声音…我该怎么玩?是 NSSound 足够快速和可靠以保持所需的准确性?

    3 回复  |  直到 13 年前
        1
  •  3
  •   Peter Hosey    14 年前

    对于大多数情况来说,nstimer是足够精确的,只要你不在任何线程上磨磨蹭蹭,计时器被安排在足够长的时间,使计时器迟到。您需要及时返回到运行循环,以便下次启动。幸运的是,nssound是异步播放的,所以这不应该是问题。

    通常情况下,当人们把时间间隔设定得很低时(我看到的一个问题是1摄氏度)。如果你所做的事情比这个时间间隔长(并且花费超过1摄氏度真的很容易),那么你将在计时器被触发后返回到运行循环,这将使计时器延迟,这将破坏你的计时。

    你只需要让你的计时器方法实现尽可能快,如果你不能使它足够快,做一个nsOperation子类来做这个工作,让你的计时器方法只是实例化这个操作,设置它,并添加到一个操作队列。(该操作将在另一个线程上运行,因此它不会占用您安排计时器运行的运行循环的线程。)如果计时器可能非常频繁,那么这是一种可能需要进行微优化(当然是由仪器和Shark引导的)的情况。

        2
  •  18
  •   Rob    13 年前

    音乐质量音频的一般信息

    这是一个需要从山顶向任何为iOS制作仪器的人呼喊的东西:

    如果您使用的是MIDI,那么您将更少地处理这些问题(声音渲染器必须处理其中的大部分问题),但所有乐器都必须处理这些要求:

    音乐家的期望

    第一件事是使audioqueue使用的缓冲区尽可能小。iPad的默认设置是 一千零二十四 样本,这是一个痛苦的长23毫秒。当你想播放一个具有缓冲区大小的声音时,即使计算机速度极快,也需要0到23毫秒的响应时间。如果缓冲区的长度超过256个样本,您将无法用它来做很多真正的工具。在实践中,还可以在其上添加多达30毫秒的开销(从触摸响应到图形等)。

    //   256 / ( 44.1khz) ~  5.8ms 
    float latency = 0.005; //in seconds gives a 256 sample buffer
    //on an infinitely fast computer, we'd make sure we got a buffer of size 1.
    // but it will take too much CPU. :-)  some latency is inevitable.
    OSStatus status = AudioSessionSetProperty(
      kAudioSessionProperty_PreferredHardwareIOBufferDuration,
      sizeof(latency),&latency
    );
    
    • 延迟-专业音乐家预计手指到耳朵的延迟约为8毫秒。对我来说,这似乎是iPad上的一个幻想,但这就是我的目标。仅仅是为了快速演奏,想想每分钟120次(四分音符)的节奏和16个音符的4倍精度,等等。即使不快速演奏,音乐家们也会因高延迟而恼火,并且不再使用超过100毫秒的伟大乐器(!!!!!)任何超过30毫秒的声音,他们都不高兴地进入专业工具和时间转移录制的声音几毫秒回来修复你的错误。商店里更好的应用程序可以达到30毫秒左右。有些非常好的应用程序在50毫秒左右仍然可以使用。

      通过将麦克风放在iPad表面上,然后将其送入混音器的左声道来测量延迟。然后把iPad放到正确的频道。把声音录成立体声。然后把它放到一个声音编辑器中,让你可以看到立体波形。你可以看到手指敲击触摸屏的时间,然后是电子声音开始的时间。这个距离就是潜伏期。它的变化是抖动的。

    • 抖动-抖动是延迟的方差。超过几毫秒的抖动会使节奏听起来错误。我大概需要5毫秒。这与音频队列缓冲区的大小有很大的直接关系。1024样本缓冲区的例子:如果您在缓冲区的某个随机时间点按下手指,那么当您需要响应时,期望值将在样本512处。所以最小值从0到1024毫秒不等,但平均值是512…在添加来自其他源的延迟之前,等待缓冲区平均完成12毫秒。

    • 触摸延迟-最重要的是尽快为ToucheSbegan提供服务。即使这意味着触摸被移动、触摸被删除、触摸被取消需要更长的时间。因此,为了实现这一点,您可能最终会在工作负载上出现一些不对称。起初,这对我来说是一个令人困惑的发现,因为分析人员告诉我,在Touchesbegan上花的时间不多,但是将一些工作推迟到Touch结束可能会有很大的不同。

    • OpenGL——你至少应该考虑一下。我不相信直接使用uikit和在我的主应用程序上使用opengl时很容易达到这些延迟目标。

    • 实时时钟-对于那些不是真正的仪器,但有严格的节拍同步要求的东西,如鼓机,你可以跟踪它的时间。 真正地 是并使样本跟踪到时钟,并假设在您不看时钟时有漂移。但我已经厌倦了用我的旧MIDI乐器打那场仗…如果你有MIDI,那么MIDI就可以处理时钟信号;它可能是唯一能使两个电子仪器同步的东西。

    • 声音跳过-所有你的应用程序竞争的CPU时间。考虑到您已经尽可能地提高了声音引擎的效率,使缓冲区变小需要更多的CPU。为了更好的连续性,您可能需要牺牲一些延迟。我有我的偏见,如果声音引擎能得到更多的循环,我就不要再胡说八道了,要量入为出。

    • 计时器。使用caDisplayLink。我没必要想太多。这就是我所拥有的。

    - (NSInteger)animationFrameInterval
    {
        return animationFrameInterval;
    }
    
    - (void)setAnimationFrameInterval:(NSInteger)frameInterval
    {
        if (frameInterval >= 16)
        {
            animationFrameInterval = frameInterval;
    
            if (animating)
            {
                [self stopAnimation];
                [self startAnimation];
            }
        }
    }
    
    - (void)startAnimation
    {
        if (!animating)
        {
          displayLink = [NSClassFromString(@"CADisplayLink")
                displayLinkWithTarget:self
                selector:@selector(drawView:)];
            [displayLink setFrameInterval:animationFrameInterval];
            [displayLink addToRunLoop:[NSRunLoop
                  currentRunLoop]
                forMode:NSDefaultRunLoopMode];
    
            animating = TRUE;
        }
    }
    
    - (void)stopAnimation
    {
        if (animating)
        {
          [displayLink invalidate];
          displayLink = nil;
    
            animating = FALSE;
        }
    }
    

    我不是在编这些东西。在解决这些问题之前,请不要在商店里摆放“乐器”。在谈论ipad/iphone应用程序时,乔丹·鲁迪斯、莱昂·格伦鲍姆、克里斯·达德利和其他专业音乐家经常提到这些问题。情况似乎有所改善,但我认为大多数开发人员不知道真正的音乐家期望的数字。

        3
  •  5
  •   sbooth    14 年前

    我怀疑您是否能够使用nstimer/nssound方法实现音频的采样精确计时。

    我认为你最好的选择是直接使用核心音频。不幸的是,学习曲线有点陡峭,因为它是一个C API,没有Cocoa那么好的文档记录。

    最简单的开始方法是audioqueue——它应该提供播放音频所需的所有功能,并为计时做准备。

    另一种选择是使用较低级别的Augraph和音频单元-一个简单的Augraph和两个音频单元-连接到默认输出单元的音频文件播放器(或预定的SoundPlayer)也可以很好地工作。