【问题标题】:Sound playing through SKAction but not through AVAudioPlayer通过 SKAction 播放声音,但不通过 AVAudioPlayer
【发布时间】:2017-02-11 18:09:10
【问题描述】:

我在游戏中播放声音时遇到了一个令人烦恼的问题,但似乎无法通过 AVAudioPlayer 获得任何输出。最初这是作为一个类设置的,但是当它不起作用时,我尝试对声音输出进行硬编码,以检查那里是否存在问题。它仍然不起作用,我尝试了几个不同的文件来排除有问题的音频文件或格式。正在检索 URL,但 audioPlayer 似乎只是没有输出声音。

我已经阅读了一些关于类似问题的帖子,并修改了系统声音设置,但这并没有什么不同。有人建议模拟器在某些情况下无法输出声音,但我不认为这是这种情况。

let myFile = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("gong", ofType: "wav")!)
    print ("URL data: \(myFile)")
    do{
        let audioPlayer = try AVAudioPlayer(contentsOfURL:myFile)
        audioPlayer.prepareToPlay()
        audioPlayer.play()    
    }catch {
        print("Error playing sound")
    }

不过这玩得很好:

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

我需要 AVAudioPlayer 的功能,因为我希望能够在循环播放时开始停止并控制声音的音量。我正在使用 XCode 7.3.1 和 Swift 2.2。

有什么想法吗?

非常感谢, 千瓦

【问题讨论】:

  • 你导入了 AVFoundation 对吗?
  • 是的。我想我解决了。我需要在一个持久的范围内的 AVAudioPlayer 上创建一个实例,因此它不会被自动垃圾收集删除。另外,我认为prepareToPlay命令在执行播放命令之前没有时间将文件加载到内存中,并且播放器也超出范围并且在退出调用它的代码块时几乎立即被收集。结果:没有机会播放。我将创建一个类,在 init() 阶段使用 prepareToPlay 将所有声音预加载到内存中,以便可以调用它们立即播放。谢谢
  • 这就是我要说的,我从未使用过prepareToPlay 方法,而且我被告知通常没有必要。
  • 这是必要的——当它加载到内存中时你会得到相当长的延迟(0.5 - 1 秒),这会停止所有其他进程。它在游戏中非常不和谐。
  • 发生这种情况的原因是因为AVAudioPlayerplay() 方法实际上是异步的——所以你应该异步调用它。很多人没有意识到这一点。请查看我发布的解释性答案。

标签: swift sprite-kit swift2


【解决方案1】:

谈到 Swift 2,我认为你应该尝试不同的方法来获取 url:

import AVFoundation

func playMusic(filename: String) {
    let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
    if (url == nil) {
        print("Could not find file: \(filename)")
        return
    }
    do { backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: url!, fileTypeHint: nil) }
    catch let error as NSError { print(error.description) }
    if let player = backgroundMusicPlayer {
        player.volume = 1
        player.numberOfLoops = -1
        player.prepareToPlay()
        player.play()
    }
}

【讨论】:

    【解决方案2】:

    我想添加一个对我有帮助的答案。 AVAudioPlayerplay() 方法是异步的,所以我实际上找到了在第一次加载到内存时播放声音文件而不会出现延迟的最佳方法是异步调用它:

    SWIFT 2

    import AVFoundation
    
    var audioPlayer: AVAudioPlayer?
    
    init() {
    
        let soundURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("FileName", ofType: "wav")!)
        if let soundURL = soundURL {
    
            do {
                self.audioPlayer = try AVAudioPlayer(contentsOfURL: soundURL)
            } catch let error as NSError {
                print(error.description)
            }
        } else {
            // Handle situation where URL isn't found
            print("Sound file URL not found")
        }
    
    }
    
    func playSound() {
        guard let audioPlayer = audioPlayer else { return }
        // Now we can play the sound asynchronously - which will eliminate any lag upon initial play
        let qualityOfServiceClass = QOS_CLASS_BACKGROUND
        let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
        dispatch_async(backgroundQueue, {
            audioPlayer.play()
        })
    
    }
    

    SWIFT 3

    import AVFoundation
    
    var audioPlayer: AVAudioPlayer?
    
    init() {
    
        let soundURL = URL(fileURLWithPath: Bundle.main.path(forResource: "FileName", ofType: "wav")!)
        if let soundURL = soundURL {
    
            do {
                self.audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
            } catch let error {
                print(error.description)
            }
        } else {
            // Handle situation where URL isn't found
            print("Sound file URL not found")
        }
    
    }
    
    func playSound() {
        guard let audioPlayer = audioPlayer else { return }
        // Now we can play the sound asynchronously - which will eliminate any lag upon initial play
        DispatchQueue.global().async {
            audioPlayer.play()
        }
    
    }
    

    这样做的想法是将某种 SoundManager 类合并到您的模型中。我通常会为我在整个应用程序中需要的每个声音创建一个像这样的类,它有一个AVAudioPlayer,我可以在加载我的 ViewController 时实例化它——这将根据它自己的init() 方法初始化各个音频播放器。然后我可以使用自己的播放方法播放每个文件。如果你有一堆不同的声音,它也可以帮助使用enum 来组织不同的文件,并有一个自定义的play() 方法将播放作为请求传入的任何文件。

    【讨论】:

    • 谢谢皮尔斯。我确实最终做了与此非常相似的事情。我创建了一个 AVAudioPlayer 对象字典,其中声音文件的文件名作为键。一个方法通过作为参数传递的文件名来定位播放器,它们会立即播放。唯一的问题是每个声音都有一个 AVAudioPlayer,因此您不能同时播放同一声音的多个实例。我通过将声音设置为在第二个播放呼叫到达而它仍在播放时重置为其开始来改善这种情况。在大多数情况下听起来很完美。希望这是有用的,谢谢大家。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多