如何在 iPhone 上精确播放声音?

Posted

技术标签:

【中文标题】如何在 iPhone 上精确播放声音?【英文标题】:How to play sound at precise moments on iPhone? 【发布时间】:2011-01-13 15:47:12 【问题描述】:

我正在努力在 iPhone 上创建一个简单的节拍器。该应用程序现在所做的是运行一个计时器,每隔 1/1000 秒 秒进入计时器的功能。然后它检查当前时间与启动应用程序的时间(我正在使用CACurrentMediaTime() 函数)。

CFTimeInterval currentTime = CACurrentMediaTime();
if (self.beatingStartTime == 0) 
    self.beatingStartTime = currentTime;

if ( (currentTime - self.beatingStartTime) >= self.timeIntevalBetweenTicks * self.internalTimerCounter ) 
    self.internalTimerCounter ++;
    // ...

如果有播放音频的好时机,使用 OpenAL 播放它的代码会被触发。

基本上就是这样。我检查了在模拟器和 2 台设备(iPad 和越狱的 iPhone 3GS)上运行时播放的声音,有一个问题 - 当我录制声音并在Reaper 软件中查看波形时,有些声音也播放了一点晚了,其中一些 - 有点太早了(即使我能理解“太晚”的部分,我真的不明白它应该如何更早地播放 - 因为应用程序每次都会检查秒数,它基本上不能比指定的时间更早——但根据我的记录,它确实如此)。

同时,有一些节拍器应用程序在计时方面以“坚如磐石”而闻名,所以我想是有办法的。我只是想知道我错过了什么......


编辑:将计时器调用从 1/1000 秒更改为例如 1/100 秒并没有帮助。

edit 2:当我从计时器切换到线程时(并且我让线程休眠指定的时间),我仍然会遇到奇怪的行为。节奏四处移动,虽然我可以理解有点滞后和播放一些声音太晚,但问题是其中一些确实播放太早 - 这意味着 2 个节拍之间的时间距离小于应该通过。

差异约为 3%,相当于大约 10-15 毫秒,这对我来说是相当多的。有人知道为什么声音可以更早播放吗?我在 iPhone 模拟器和 iPad 实际设备上都试过了,我唯一的猜测是计时器有问题 - CACurrentMediaTime() 返回更多的秒数。甚至可能吗?

【问题讨论】:

你为什么使用 OpenAL 而不是简单的 AVAudioPlayer? (您的节拍器需要定位音频吗?) 不。但是,当我使用 AVAudioPlayer 之前,我遇到了类似的问题(或者,甚至更糟)。我找到了使用 OpenAL 的建议,因为它最适合在特定时刻播放声音(与粒子游戏帧同步等)。 - 此博客条目benbritten.com/2008/11/06/openal-sound-on-the-iphone 提到“为了更好地控制声音,您将需要 openAL 或 audioUnits 或 audioQueue”)。 @kender,另外,您不能每秒调用 1000 次计时器并要求它响应。你在自找麻烦,这就是为什么它似乎永远不会达到目标的原因。即使您将节拍器设置为 16 分音符,速度为 240,您也只需要一个分辨率为 0.015625 秒的计时器。如果不运行您自己的 c 库,您将无法在 iphone 上获得毫秒级的精度,然后就会出现问题。 @Kender,你试过 [NSThread setThreadPriority:1.0];如果你优先考虑线程,它应该会稍微稳定你的结果,但是我不相信使用 Core Animation 来处理你的时间是要走的路。动画不需要像您使用它的那样精确。 那该用什么?系统时间甚至更不稳定(并且可以在 iPhone 上移动,例如,当它与网络同步时......) 【参考方案1】:

尝试使用 NSSound,并将其作为实例变量加载,除非您的节拍器没有运行,否则不要释放它。在循环中将文件加载到内存中可能会导致延迟。要考虑的另一件事是节拍器可能不需要每 1/1000 秒轮询一次。如果您不经常这样做,CPU 饱和的可能性就会降低,并且您可能会获得更一致的结果。

最后,看看 Apple 的演示是如何工作的:http://developer.apple.com/library/ios/#samplecode/Metronome/Introduction/Intro.html

可能会让您更好地了解如何完成您正在尝试做的事情:)

【讨论】:

现在我正在使用 OpenAL,我的声音会在节拍器启动时被读入实例变量,因此不需要在每次迭代时从文件中读取它们。【参考方案2】:

您想要的是COCOS DENSHION,它是一个简单可靠且易于使用的声音库,我们发现它可以解决所有问题。

我(只是个人)不喜欢“Cocos2D”,但您可以直接使用 CocosDenshion。

其次——对于计时器来说,1000 秒是荒谬的。完全忘记它。

第三——你发现 AVAudioPlayer 一文不值。

注意 - “ObjectAL”是一种新的,或许更好的替代方法

到 CocosDenshion。看看吧。

【讨论】:

以上是关于如何在 iPhone 上精确播放声音?的主要内容,如果未能解决你的问题,请参考以下文章

在 iPhone 上振动时播放自定义声音

如何在通话期间以编程方式在 iPhone 的两个扬声器上播放声音

在 iPhone 中接收推送通知时如何播放声音

如何在 iPhone 应用程序中录制和播放声音?

无法使用 JavaScript 在 iPhone 上播放声音,但可以在 Android 上播放

如何在Objective C iphone编码中播放声音