【问题标题】:iOS: How to play audio without fps drops?iOS:如何在没有 fps 下降的情况下播放音频?
【发布时间】:2016-11-23 20:43:58
【问题描述】:

我正在使用 Sprite Kit 为 iOS 9+ 开发游戏,最好使用 Swift 库。

目前我正在使用 Singleton 来预加载我的音频文件,每个文件都连接到一个单独的 AVAudioPlayer 实例。

这里有一个简短的代码片段来了解这个想法:

import SpriteKit
import AudioToolbox
import AVFoundation

class AudioEngine {

    static let sharedInstance = AudioEngine()

    internal var sfxPing: AVAudioPlayer

    private init() {

        self.sfxPing = AVAudioPlayer()

        if let path = NSBundle.mainBundle().pathForResource("ping", ofType: "m4a") {
            do {
                let url = NSURL(fileURLWithPath:path)
                sfxPing = try AVAudioPlayer(contentsOfURL: url)
                sfxPing.prepareToPlay()
            } catch {
                print("ERROR: Can't load ping.m4a audio file.")
            }
        }

    }

}

此 Singleton 在应用启动期间初始化。在游戏循环中,我只需调用以下行来播放特定的音频文件:

AudioEngine.sharedInstance.sfxPing.play()

这基本上是可行的,但是当我在 iPad Air 上播放文件并且帧速率从 60.0 下降到 56.0 时,我总是会出现故障。

有人知道如何用 AVAudioPlayer 解决这个性能问题吗?

我还留意了 3rd 方库,即:

要求:

  • 播放大量非常短的样本(如击球、击球等)
  • 播放一些运动效果(这样投球会很好)
  • 循环播放一些背景/环境声音
  • 没有令人讨厌的故障/帧率下降!

您能否根据我的要求推荐上述任何库或使用上述代码指出问题?

更新:

播放短音:

self.runAction(SKAction.playSoundFileNamed("sfx.caf", waitForCompletion: false))

确实提高了帧速率。 I exported the audio files with Audiacity to the .caf format (Apple's Core Audio Format). 但在本教程中,它们使用“Signed 32-bit PCM”编码导出,这在我的情况下会导致音频播放受到干扰。使用任何其他编码选项(32-bit floatU-LawA-Law 等)对我来说都很好。

为什么使用caf 格式?因为它是未压缩的,因此与m4a 等压缩格式相比,它以更少的 CPU 开销更快地加载到内存中。对于在很短的时间间隔内播放很多短音效,这是有道理的,对于消耗几千字节的短音频文件,磁盘使用不会受到太大影响。对于较大的音频文件,例如环境音乐和背景音乐,使用压缩格式(mp3m4a)显然是更好的选择。

【问题讨论】:

  • stackoverflow.com/questions/25646583/… 这可能会有所帮助:)
  • @Konsy:感谢您的评论。我知道这个链接,因为我自己发布了一个答案;-) 但我的问题无法通过循环解决。这对于像马达或火箭推力这样的音效很有用,但是我的音效会反复播放,没有像用户开枪那样的精确模式。对问题中列出的框架有任何进一步的想法和/或建议吗?跨度>
  • 使用Sprite Kit的方法怎么样?喜欢SKAction.playSoundFileNamed。我没有测试自己,但我认为它已针对不降低帧速率进行了优化。可能值得一试。
  • @Giorgio 对不起,我不记得了。从那时起很久以前。这些问题不是同时在较新的 SDK 中得到解决了吗??
  • @Giorgio 真的很抱歉我帮不了你。太久了。我只记得我还必须实施各种讨厌的黑客攻击。我相信以秒为单位存储效果的播放长度然后阻止我的 play() 函数,只要在播放另一个文件之前播放文件......这样的东西:-/

标签: ios swift audio avaudioplayer audiokit


【解决方案1】:

根据您的问题,如果您为 iOS 9+ 开发游戏,您可以使用新的 iOS 9 库 SKAudioNode (official Apple doc):

var backgroundMusic: SKAudioNode!

例如,您可以将其添加到didMoveToView()

if let musicURL = NSBundle.mainBundle().URLForResource("music", withExtension: "m4a") {
    backgroundMusic = SKAudioNode(URL: musicURL)
    addChild(backgroundMusic)
}

你也可以用它来播放一个简单的效果:

let beep = SKAudioNode(fileNamed: "beep.wav")
beep.autoplayLooped = false
self.addChild(beep)

最后,如果你想改变音量:

beep.runAction(SKAction.changeVolumeTo(0.4, duration: 0))

更新

我看到你更新了关于AVAudioPlayerSKAction 的问题。我已经针对我的 iOS8+ 兼容游戏测试了它们。

关于AVAudioPlayer,我个人使用的是我根据旧的SKTAudio制作的自定义库。

我看到你的代码,关于 AVAudioPlayer init,我的代码是不同的,因为我使用:

@available(iOS 7.0, *)
    public init(contentsOfURL url: NSURL, fileTypeHint utiString: String?)

我不知道fileTypeHint 是否会有所作为,所以请尝试向我介绍您的测试。

您的代码的优势: 使用基于AVAudioPlayer 的共享实例音频管理器,您可以控制音量,随时随地使用您的管理器,确保与 iOS8 兼容

您的代码的缺点: 每次你播放一个声音并想播放另一个声音时,前一个声音就会被破坏,尤其是如果你启动了背景音乐。

如何解决?根据这个 SO post 工作良好没有问题似乎 AVFoundation 仅限于 4 AVAudioPlayer 属性实例化,所以你可以这样做:

  • 1) 背景音乐播放器:AVAudioPlayer!
  • 2) soundEffectPlayer1:AVAudioPlayer!
  • 3) soundEffectPlayer2:AVAudioPlayer!
  • 4) soundEffectPlayer3:AVAudioPlayer!

你可以构建一个方法,通过3个soundEffect切换来查看是否被占用:

if player.playing

并使用下一个免费播放器。使用此解决方法,您的声音始终可以正确播放,甚至是背景音乐。

【讨论】:

  • 非常感谢您的回答。我会立即检查这种方法,如果我没有遇到性能问题并且完全满足我的要求,我会检查你的答案作为解决方案。
  • 我检查了您的答案作为解决方案并更新了我的原始问题。谢谢。
  • 好的,你可以选择和我一样的方式,因为我的游戏是iOS 8+,我会尽快给你答复..
  • 好的,谢谢。我刚刚更新了问题,请在“更新”下查看...
  • 感谢您的更新。目前我需要以更高的优先级处理我游戏的其他部分,因为我的搭档无法在其他情况下处理游戏地图。但是这个音频问题肯定会重新出现在桌面上……我会在回到这个话题后立即向这个线程报告我的发现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多