【问题标题】:How can I allow background music to continue playing while my app still plays its sounds while using Swift如何让背景音乐继续播放而我的应用程序在使用 Swift 时仍然播放其声音
【发布时间】:2015-03-13 03:17:32
【问题描述】:

我创建了一个应用程序,我试图让用户在玩我的游戏时继续听他们的音乐,但只要他们点击“播放”并且游戏中的声音出现,它就会停止背景音乐。我正在使用 Swift 在 iOS 上进行开发。这是一段启动游戏声音的代码。

func playSpawnedDot() {
    var alertSound: NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("spawnDot", ofType: "mp3")!)!
    var error:NSError?
    audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
    audioPlayer.prepareToPlay()

    if volumeBool {
        audioPlayer.play()
    }
}

【问题讨论】:

    标签: ios xcode swift audio mobile


    【解决方案1】:

    您需要将AVAudioSession 类别设置为以下值之一:https://developer.apple.com/library/ios/documentation/AVFoundation/Reference/AVAudioSession_ClassReference/index.html(AVAudioSession 类参考)。

    默认值设置为AVAudioSessionCategorySoloAmbient。如您所见:

    [...] 使用此类别意味着您的应用程序的音频是不可混合的——激活您的会话将中断任何其他同样不可混合的音频会话。要允许混合,请改用AVAudioSessionCategoryAmbient 类别。

    在播放声音之前,您必须更改类别。这样做:

    AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, error: nil)
    AVAudioSession.sharedInstance().setActive(true, error: nil)
    

    您无需在每次播放声音时都调用这些线路。你可能只想做一次。

    【讨论】:

    • 这在 swift 2.0 中不再有效。有更新的代码吗?
    • 这对我有用 { try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient) try AVAudioSession.sharedInstance().setActive(true) } catch let error as NSError { print(error) }跨度>
    • 如果您无法将此代码适应新版本的 Swift,请尝试使用以下代码:try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient)
    【解决方案2】:

    Swift 4 版本:

    try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient)
    try? AVAudioSession.sharedInstance().setActive(true)
    

    【讨论】:

    • 别忘了import AVFoundation
    • 一旦你的声音播放完毕,不要忘记将setActive设置为false,否则,即使播放完成,背景音乐也会保留。
    【解决方案3】:

    这就是我在 Swift 3.0 中的做法

    var songPlayer : AVAudioPlayer?         
    
    func SetUpSound() {
    
    
            if let path = Bundle.main.path(forResource: "TestSound", ofType: "wav") {
                let filePath = NSURL(fileURLWithPath:path)
                songPlayer = try! AVAudioPlayer.init(contentsOf: filePath as URL)
                songPlayer?.numberOfLoops = -1 //logic for infinite loop
                songPlayer?.prepareToPlay()
                songPlayer?.play()
            }
    
            let audioSession = AVAudioSession.sharedInstance()
            try!audioSession.setCategory(AVAudioSessionCategoryPlayback, with: AVAudioSessionCategoryOptions.duckOthers) //Causes audio from other sessions to be ducked (reduced in volume) while audio from this session plays  
    }
    

    您可以在此处查看更多 AVAudioSessionCategoryOptions:https://developer.apple.com/reference/avfoundation/avaudiosessioncategoryoptions

    【讨论】:

    • 将 (let audioSession...) AND (try!audioSession...) 行移到 if 条件之上以获得更好的结果。背景音乐也不会第一次停止。 (我在按钮操作中插入了 func SetUpSound() 代码)
    • @Hamid Reza Ansari - 你的评论拯救了我的一天。谢谢!
    【解决方案4】:

    这是我在 Swift 2.0 中使用的:

    let sess = AVAudioSession.sharedInstance()
    if sess.otherAudioPlaying {
        _ = try? sess.setCategory(AVAudioSessionCategoryAmbient, withOptions: .DuckOthers)
        _ = try? sess.setActive(true, withOptions: [])
    }
    

    请注意,如果您不想降低背景音乐并在上面播放,可以将.DuckOthers 替换为[]。

    【讨论】:

      【解决方案5】:

      lchamp 的解决方案非常适合我,适用于 Objective-C:

      [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
      [[AVAudioSession sharedInstance] setActive:YES error:nil];
      

      【讨论】:

        【解决方案6】:

        **

        为 Swift 3.0 更新

        **

        我正在播放的声音的名称是 shatter.wav

        func shatterSound() {
            if let soundURL = Bundle.main.url(forResource: "shatter", withExtension: "wav") {
                var mySound: SystemSoundID = 0
                AudioServicesCreateSystemSoundID(soundURL as CFURL, &mySound)
                AudioServicesPlaySystemSound(mySound);
            }
        }
        

        然后你想在哪里播放声音呼叫

        shatterSound()
        

        【讨论】:

          【解决方案7】:

          因为他们似乎无法从一个版本到另一个版本下定决心。这是在 Swift 5.0 中

          do{
             try AVAudioSession.sharedInstance().setCategory(.ambient)
             try AVAudioSession.sharedInstance().setActive(true, options: .notifyOthersOnDeactivation)
          } catch {
             NSLog(error.localizedDescription)
          }
          

          【讨论】:

            【解决方案8】:

            如果你想播放提示音:

            public func playSound(withFileName fileName: String) {
            
                if let soundUrl = Bundle.main.url(forResource: fileName, withExtension: "wav") {
                    do {
                        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, with:[.duckOthers])
                        try AVAudioSession.sharedInstance().setActive(true)
            
                        var soundId: SystemSoundID = 0
            
                        AudioServicesCreateSystemSoundID(soundUrl as CFURL, &soundId)
                        AudioServicesAddSystemSoundCompletion(soundId, nil, nil, { (soundId, clientData) -> Void in
                            AudioServicesDisposeSystemSoundID(soundId)
                            do {
                                // This is to unduck others, make other playing sounds go back up in volume
                                try AVAudioSession.sharedInstance().setActive(false)
                            } catch {
                                DDLogWarn("Failed to set AVAudioSession to inactive. error=\(error)")
                            }
                        }, nil)
            
                        AudioServicesPlaySystemSound(soundId)
                    } catch {
                        DDLogWarn("Failed to create audio player. soundUrl=\(soundUrl) error=\(error)")
                    }
                } else {
                    DDLogWarn("Sound file not found in app bundle. fileName=\(fileName)")
                }
            }
            

            如果你想播放音乐:

            导入 AVFoundation

            var audioPlayer:AVAudioPlayer?
            
            public func playSound(withFileName fileName: String) {
            
                if let soundUrl = Bundle.main.url(forResource: fileName, withExtension: "wav") {
                    do {
                        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, with:[.duckOthers])
                        try AVAudioSession.sharedInstance().setActive(true)
            
                        let player = try AVAudioPlayer(contentsOf: soundUrl)
                        player.delegate = self
                        player.prepareToPlay()
                        DDLogInfo("Playing sound. soundUrl=\(soundUrl)")
                        player.play()
                        // ensure the player does not get deleted while playing the sound
                        self.audioPlayer = player
                    } catch {
                        DDLogWarn("Failed to create audio player. soundUrl=\(soundUrl) error=\(error)")
                    }
                } else {
                    DDLogWarn("Sound file not found in app bundle. fileName=\(fileName)")
                }
            }
            
            func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
                self.audioPlayer?.stop()
                do {
                    // This is to unduck others, make other playing sounds go back up in volume
                    try AVAudioSession.sharedInstance().setActive(false)
                } catch {
                    DDLogWarn("Failed to set AVAudioSession inactive. error=\(error)")
                }
            }
            

            【讨论】:

              【解决方案9】:

              对于 Swift(Objective-C 也是这样)

              您可以使用this 链接获得最佳答案,如果您没有时间观看 10 分钟,最好的做法是在 didFinishLaunchingWithOptions 中的 AppDelegate 中的代码下方 copy,然后选择你的project's target 然后转到Capabilities 最后在Background modes 检查Audio, AirPlay and Picture in Picture

              func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
                  // Override point for customization after application launch.
              
                  let session = AVAudioSession.sharedInstance()
                  do {
                      try session.setCategory(AVAudioSessionCategoryPlayback)
                  }
                  catch {
              
                  }
              }
              

              【讨论】:

                【解决方案10】:

                我不认为只是将 AVAudioSession 设置为 AVAudioSessionCategoryOptions.duckOthers 会起作用,但确实如此。这是我的完整代码,可以帮助像我这样的新手。

                Swift 4 - 完整示例:

                var audioPlayer = AVAudioPlayer()
                
                
                func playSound(sound: String){
                    let path = Bundle.main.path(forResource: sound, ofType: nil)!
                    let url = URL(fileURLWithPath: path)
                
                    let audioSession = AVAudioSession.sharedInstance()
                    try!audioSession.setCategory(AVAudioSessionCategoryPlayback, with: AVAudioSessionCategoryOptions.duckOthers)
                
                    do {
                        audioPlayer = try AVAudioPlayer(contentsOf: url)
                        audioPlayer.play()
                    } catch {
                        print("couldn't load the file")
                    }
                }
                

                我仍然需要弄清楚 setActive (I was looking at this blog post),但是当我离开应用程序时音频停止闪避,因此它适用于我的应用程序。

                【讨论】:

                  【解决方案11】:

                  Swift 5 版本基于 lchamp 的回答。

                  try? AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient)
                  try? AVAudioSession.sharedInstance().setActive(true)
                  

                  【讨论】:

                    【解决方案12】:

                    这对我来说非常适合 iOS 14.4 和 Swift 5。使用.duckOthers 选项与其他人的答案有点不同(如果您愿意,也可以直接与声音混合使用.mixWithOthers),但它在播放音乐时完美运行。它将降低音乐音量,播放“哔”声,然后将音乐音量恢复到正常水平。如果出现问题,它还会使用 Google Firebase Crashlytics 捕获错误数据,并尝试在出现错误时将音量提高到正常水平。

                    此代码也可以在您的声音的第一次和所有其他播放中完美运行,而不会停止音乐。

                    func playSound() {
                        do {
                            if let path = Bundle.main.path(forResource: "beep", ofType: "mp3") {
                                try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: .duckOthers)
                                try AVAudioSession.sharedInstance().setActive(true)
                                
                                let filePath = NSURL(fileURLWithPath:path)
                                songPlayer = try AVAudioPlayer.init(contentsOf: filePath as URL)
                                songPlayer?.numberOfLoops = 0
                                songPlayer?.prepareToPlay()
                                songPlayer?.play()
                                
                                try AVAudioSession.sharedInstance().setActive(false)
                            }
                        } catch (let error) {
                            try? AVAudioSession.sharedInstance().setActive(false)
                            Crashlytics.crashlytics().setCustomValue(error.localizedDescription, forKey: "audio_playback_error")
                        }
                    }
                    

                    【讨论】:

                    • 这不会立即停用音频会话而不播放声音吗?
                    【解决方案13】:

                    其他答案没有的重要一点是,您需要在停用时使用 .notifyOthers 标志调用 false:

                    try AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
                    

                    这样做的原因是,当您停用您的应用程序时,其他在后台播放音乐的应用程序会知道何时重新打开其音频。否则,如果您关闭会话,您的背景音乐将不会重新打开。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-10-03
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2011-11-23
                      • 1970-01-01
                      相关资源
                      最近更新 更多