AVAudioPlayer声音无法播放

9
在iOS 8 / Xcode 6中,我有一个包含声音效果的函数。在多次更改代码后,它在iOS 9上不再起作用。这是我尝试过的内容:
原始内容:
let bangSoundEffect = SKAction.playSoundFileNamed("Bang.mp3", waitForCompletion: false)
        runAction(bangSoundEffect)

其他尝试:

self.runAction(SKAction.playSoundFileNamed("Bang.mp3", waitForCompletion: false))

此外:
func playRocketExplosionSound(filename: String) {
            let url = NSBundle.mainBundle().URLForResource(
                filename, withExtension: nil)
            if (url == nil) {
                print("Could not find file: \(filename)")
                return }
            var error: NSError? = nil
            do {
                backgroundMusicPlayer =
                    try AVAudioPlayer(contentsOfURL: url!)
            } catch let error1 as NSError {
                error = error1
                backgroundMusicPlayer = nil
            }
            if backgroundMusicPlayer == nil {
                print("Could not create audio player: \(error!)")
                return}
            backgroundMusicPlayer.numberOfLoops = 1
            backgroundMusicPlayer.prepareToPlay()
            backgroundMusicPlayer.play() }



playRocketExplosionSound("Bang.mp3")

我为这个问题烦恼了好久。在另一个场景中使用同样的代码播放其他音效时却没问题!出了什么问题? 我注意到有时候在模拟器中开始播放音效,但是没有完成并且报错:

2015-09-24 19:12:14.554 APPNAME[4982:270835] 19:12:14.553 ERROR:    177: timed out after 0.012s (735 736); mMajorChangePending=0

实际设备上完全无法工作。

问题出在哪里?:'(


我在这里提供了导致问题的完整函数:http://stackoverflow.com/questions/32723431/skaction-playsoundfilenamed-not-working-in-ios-9 - JGrn84
不要在模拟器中测试吗? - matt
它在实际设备上根本不起作用。 - JGrn84
1
很难说。通常在视频游戏中,对于那种音效,你会希望避免任何有损压缩格式,因为它们需要时间解码。为什么不把“砰”的声音制成WAV格式?我从未见过使用MP3作为短音效的视频游戏。我相当确定苹果可以硬件加速解码一个音轨,但可能无法同时处理多个音轨(WAV文件无需解码)。如果这不能解决问题,那么问题就在别处,也许是SK在某些情况下掐灭了声音。 - Eppilo
如果代码适用于其他声音,则我会查看资源本身。删除它并重新添加,确保将项目复制到目标组文件夹中。 - john elemans
根据其他评论和答案,似乎你所使用的mp3文件有问题。 - SwiftArchitect
3个回答

8

可能存在MP3文件问题

问题很可能与您正在使用的MP3文件有关。代码可以用于其他声音,这表明MP3文件可能已损坏,而AVAudioPlayer无法解码它。您可以尝试重新编码此文件并查看问题是否仍然存在。或者更好的方法是将其转换为WAV

使用WAVs

在创建游戏短音效时,一般的经验法则是使用WAV,除非您真的需要减少文件大小。

顶级游戏追求高端制作质量,因此会以未压缩的方式记录和制作资源,采用24位/48kHz。稍微低一些的游戏可能会采用16/44.1,这是CD音频的官方标准。

这至少有两个好处。一是声音质量更好。第二个好处是CPU不必解码文件即可播放它。


我不是原帖作者,但我添加了悬赏...我从外部网络服务获取我的mp3作为NSData,因此偶尔会出现损坏的文件并非不可能。我没有切换格式的选项。对于错误消息“ERROR: 177: timed out after 0.012s (735 736); mMajorChangePending=0”有什么想法吗? - picciano
我给这个答案点了赞。即使文件格式不是问题,正确的调试方式是尝试使用一个示例来进行调试。据我所知,这个具体错误在任何地方都没有记录,但它似乎与使用任何API播放声音相关。我认为必须探索不同的路线,在视频游戏编程中这是一个明显的路线(声音引擎也有限制通道以及一个明确的苹果限制:音频解码一次只能解码一个轨道)。 - Eppilo

4

数据文件损坏 | AVAudioPlayer 超出作用域


1. 数据文件损坏

这将确保您已找到该文件:

    var backgroundMusicPlayer: AVAudioPlayer? = nil

    if let url = Bundle.main.url(
        forResource: "Bang", withExtension: "mp3") {
        do {
            try backgroundMusicPlayer = AVAudioPlayer(contentsOf: url)
            backgroundMusicPlayer!.play()
        } catch {}
    }
    return nil

2. AVAudioPlayer 不在范围内

play() 完成并返回之前,保留 backgroundMusicPlayer 的变量不能超出范围。通常可以通过使用类变量来实现此目的:

var backgroundMusicPlayer: AVAudioPlayer? = nil
不要这样做: 由于变量audioPlayer的本地作用域,以下声音最多只会在outOfScopeDelay时播放。
let outOfScopeDelay = 0.5
do {
    var audioPlayer:AVAudioPlayer! // Incorrectly scoped variable
    try audioPlayer = AVAudioPlayer(contentsOf: audioRecorder.url)
    audioPlayer.play()
    Thread.sleep(forTimeInterval: outOfScopeDelay)
} catch {}

► 您可以在GitHub上找到此解决方案,并在Swift Recipes上了解更多详细信息。


那只是捕捉错误,而不能确定错误的原因,对吗?我正在寻求关于错误"ERROR: 177: timed out after 0.012s (735 736); mMajorChangePending=0"的解释,以及如何避免它而不仅仅是捕捉它。谢谢! - picciano
你是否验证了你没有错误地限定了 AVAudioPlayer?请参考 https://dev59.com/VlwY5IYBdhLWcg3wwqA5#32827915 - SwiftArchitect

3

试试这个:

dispatch_async(dispatch_get_main_queue(), {
                        (self.playRocketExplosionSound("Bang.mp3")
                        })

iOS 9之后,在子线程中播放音频已经不再安全。


1
有趣,你能提供一个支持这个说法的文档链接吗?谢谢! - picciano
问题在于苹果公司对他们的问题非常害羞,因此我们只能根据经验来解决问题,没有相关问题的文档可供参考。 - Allen
1
我不同意。在后台播放是可行的。你只需要保持AVAudioPlayer的引用足够长,让声音播放完成即可。 - SwiftArchitect
1
我同意@SwiftArchitect的观点。AVAudioPlayer的播放方法是异步的,但似乎可以在后台线程中正常工作。 - jk7

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接