【问题标题】:AVAudioPlayer produces lag despite prepareToPlay() in Swift尽管在 Swift 中使用 prepareToPlay() ,但 AVAudioPlayer 仍会产生延迟
【发布时间】:2014-10-19 15:35:16
【问题描述】:

在我用 Swift 编程的 SpriteKit iOS 游戏中播放非常短的声音(约 0.5 秒)会产生打嗝(如延迟)。在其他问题中,我读到我应该prepareToPlay() 声音,我做到了。

我什至使用了一个变量 (soundReady) 来检查声音在播放之前是否已准备好。每次播放完毕时,我都会重新准备声音 (audioPlayerDidFinishPlaying())。以下是代码的相关部分:

class GameScene: SKScene, AVAudioPlayerDelegate {

   var splashSound = NSURL()
   var audioPlayer = AVAudioPlayer()
   var soundReady = false

   override func didMoveToView(view: SKView) {
      let path = NSBundle.mainBundle().pathForResource("plopSound", ofType: "m4a")
      splashSound = NSURL(fileURLWithPath: path)
      audioPlayer = AVAudioPlayer(contentsOfURL: splashSound, error: nil)
      audioPlayer.delegate = self
      soundReady = audioPlayer.prepareToPlay()
   }

   func playSound(){
      if(soundReady){
         audioPlayer.play()
         soundReady = false
      }
   }

   func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool){
      //Prepare to play after Sound finished playing
      soundReady = audioPlayer.prepareToPlay()
   }
}

我不知道我在这个问题上哪里出错了。我感觉我什么都试过了(包括但不限于:只准备一次,玩完就准备,不使用变量,只使用prepareToPlay())。

附加信息:

  • 声音立即播放。
  • 在最后一次完成后播放声音的速度似乎不会影响延迟。

【问题讨论】:

  • 不会像 UIViewController.ViewDidAppear() 那样触发 DidMoveToView 吗?也就是说,它只有在场景完全准备好后才会触发。 Apple 文档现在似乎很无聊,所以我会检查一下。
  • 你试过在另一个线程上玩吗?

标签: ios audio swift avaudioplayer lag


【解决方案1】:

当我多次调用 play 时,会导致访问错误。我相信播放器正在被释放,因为这不是线程安全的。我创建了一个串行队列来缓解这个问题。

class SoundManager {

    static let shared = SoundManager()

    private init() {
        try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
        try? AVAudioSession.sharedInstance().setActive(true)
    }

    private let serialQueue = DispatchQueue(label: "SoundQueue", qos: .userInitiated)
    private var player: AVAudioPlayer?

    static func play(_ sound: Sound) {
        shared.play(sound)
    }

    func play(_ sound: Sound) {
        guard let url = Bundle.main.url(forResource: sound.fileName, withExtension: "mp3")
            else { return }

        do {
            try serialQueue.sync {
                self.player = try AVAudioPlayer(contentsOf: url, fileTypeHint: AVFileTypeMPEGLayer3)
                DispatchQueue.main.async {
                    self.player?.play()
                }
            }
        } catch let error as NSError {
            print("error: \(error.localizedDescription)")
        }
    }

}

【讨论】:

  • 我有一个声音很重的游戏和 T.R. Bremm (brilliantairic) 的解决方案在没有额外串行队列框架的情况下在 macOS 和 iOS 上对我来说都很好。
【解决方案2】:

只需添加来自@brilliantairic 的解决方案的 Swift 3 版本。

DispatchQueue.global().async {
    audioPlayer.play()
}

【讨论】:

    【解决方案3】:

    我遇到了同样的问题,并在 backgroundQueue 中播放了声音。

    这是一个很好的例子:https://stackoverflow.com/a/25070476/586204

    let qualityOfServiceClass = QOS_CLASS_BACKGROUND
    let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
    dispatch_async(backgroundQueue, {
        audioPlayer.play()
    })
    

    【讨论】:

    • 非常感谢@brilliantairic!不过我更喜欢单线:dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)) {
    • 非常感谢你救了我!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    • 2014-11-09
    • 2012-01-12
    • 1970-01-01
    相关资源
    最近更新 更多