【问题标题】:execute functions with millisecond accuracy based on timer基于定时器以毫秒精度执行函数
【发布时间】:2012-04-21 07:06:37
【问题描述】:

我有一个浮点数组,它们表示基于一段时间内触发事件的时间值。

0: time stored: 1.68
1: time stored: 2.33
2: time stored: 2.47
3: time stored: 2.57
4: time stored: 2.68
5: time stored: 2.73
6: time stored: 2.83
7: time stored: 2.92
8: time stored: 2.98
9: time stored: 3.05

我现在想启动一个计时器,当计时器达到 1 秒 687 毫秒(数组中的第一个位置)时,触发事件/方法执行。 当计时器达到 2 秒和 337 毫秒时触发第二个方法执行,直到数组中的最后一个元素在 3 秒和 56 毫秒时触发最后一个事件。

我怎样才能模仿这样的东西?我需要高精度的东西

我想我基本上要问的是如何创建一个具有高精度方法调用的节拍器以按时播放声音?

【问题讨论】:

    标签: objective-c timer precision


    【解决方案1】:

    ...如何创建具有高精度方法调用的节拍器以按时播放声音?

    您将使用音频时钟,它具有您通常想要的所有精度(音频播放的采样率 - 例如 44.1kHz) - 而不是 NSTimer

    具体来说,您可以使用采样器(例如 AudioUnit)并安排 MIDI 事件,或者您可以在由节奏确定的采样位置处的音频流回调中使用(预加载的)点击声音的采样数据填充缓冲区。

    要保持 1 毫秒或更好,您需要始终以音频时钟为基准。这真的很容易,因为您的节奏将决定帧的间隔。

    如果您以前没有在该领域工作过,那么困难的部分(对于大多数人来说)是习惯于在实时环境中工作并使用音频框架。

    【讨论】:

      【解决方案2】:

      查看dispatch_after()。您可以使用 dispatch_time(DISPATCH_TIME_NOW, 1.687000 * NSEC_PER_SEC) 之类的内容为其创建目标时间。


      更新:如果您想在特定时间播放声音,而不是做任意工作,那么您应该使用允许您在特定时间安排播放的音频 API。我最熟悉Audio Queue API。您将创建一个队列并创建 2 或 3 个缓冲区。 (如果音频始终相同,则为 2。如果您动态加载或计算它,则为 3。)然后,您将使用 AudioQueueEnqueueBufferWithParameters() 以特定的开始时间对每个缓冲区进行排队。然后,音频队列将负责播放尽可能接近请求的开始时间。我怀疑您是否会通过手动编码替代方案来超越其精度。当队列将处理过的缓冲区返回给您时,您可以在必要时重新填充它并在下一次将其排队。

      我确定AVFoundation 必须有类似的工具来安排在特定时间播放,但我不熟悉它。

      【讨论】:

      • dispatch_after() 是一个 Grand Central Dispatch 函数。最好查看 Grand Central Dispatch 文档而不是 dispatch_after() 的手册页。 developer.apple.com/library/mac/#documentation/Performance/…
      • 我不相信这是一个高度准确的替代品。原因如下:实现只是在指定时间过去后将块异步添加到队列中。这样考虑:如果队列(甚至系统)有很多工作要做,那么在实际执行块(或工作)之前可能需要相当长的时间。因此,它不能替代音频计时器。然而,音频回调在实时高优先级线程上运行并具有稳定的时间表示。即使在高工作负载下,使用音频回调的机会也会好得多。 (续)
      • (续)此外,音频在其自己的时基上运行。这意味着这种方法的准确性在许多情况下也受到渲染块大小的限制——因为它们使用不同的时基。人们会尝试通过减小缓冲区大小来弥补这种不准确性(需要更多资源并经常导致更多问题,例如丢失)。在未正确同步时减小缓冲区大小以提高准确性是一种蛮力解决方案。
      • (续)结合了这两个问题,偏差/错误可能超过 100 毫秒(并且可能更大)。
      • @Justin,您可以将dispatch_after() 定位到高优先级全局队列。这至少使您获得了更高的优先级。而且我不确定使用音频线程是否有帮助,因为您必须将任何有意义的工作分流到另一个线程,这可能会引入类似的延迟。 (您必须准确地分流它,因为音频线程是实时的。)
      【解决方案3】:

      要获得高精度时序,您必须降低一两个编程级别并利用类似Core Audio Unit 框架的东西,它提供了精确的采样时序(在 44100kHz 时,采样应该大约每 0.02 毫秒发生一次)。

      这种方法的缺点是为了获得这样的计时性能,Core Audio Unit 编程避开了 Objective-C 的 C/C++ 方法,在我看来,这比 Objective-C 更难编码。除此之外,Core Audio Units 的工作方式也非常令人困惑,尤其是如果您没有音频 DSP 方面的背景。

      在 Objective-C 中,你可能知道 NSTimers 在这里不是一个选项。为什么不查看AVFoundation framework?它可用于精确的媒体排序,并带有一些创造性的侧身思考,以及AVURLAssetAVURLAssetPreferPreciseDurationAndTimingKey 选项,您也许可以在不使用Core Audio Units 的情况下实现您想要的。


      只是要填写更多关于AVFoundation的信息,您可以将AVAsset的实例放入AVMutableComposition(通过AVMutableCompositionTrack对象),然后使用AVPlayerItem对象和AVPlayer实例来控制结果。 AVPlayerItem 通知AVPlayerItemDidPlayToEndTimeNotification (docs) 可用于确定单个资产何时完成,AVPlayer 方法addBoundaryTimeObserverForTimes:queue:usingBlock:addPeriodicTimeObserverForInterval:queue:usingBlock: 可以在任意时间提供通知。

      在 iOS 中,如果您的应用将播放音频,您还可以让这一切在后台线程上运行,这意味着您可以在应用处于后台时保持时间(但如果它不播放音频,则会发出警告) , Apple 可能不接受您的应用程序使用此后台模式)。查看UIBackgroundModes docs 了解更多信息。

      【讨论】:

        猜你喜欢
        • 2020-12-02
        • 2016-02-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多