在使用 Swift 时,如何让背景音乐继续播放,而我的应用程序仍在播放它的声音

How can I allow background music to continue playing while my app still plays its sounds while using Swift

我创建了一个应用程序,我试图让用户在玩我的游戏时继续听他们的音乐,但是每当他们点击 "play" 并且游戏中的声音出现时,背景音乐就会停止。我正在使用 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()
    }
}

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

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

[...] using this category implies that your app’s audio is nonmixable—activating your session will interrupt any other audio sessions which are also nonmixable. To allow mixing, use the AVAudioSessionCategoryAmbient category instead.

在播放声音之前,您必须更改类别。为此:

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

您不需要在每次播放声音时都调用这些行。你可能只想做一次。

这是我在 Swift 2.0 中使用的内容:

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

请注意,如果您不想降低背景音乐的音量,而是想在背景音乐之上播放,您可以将 .DuckOthers 替换为 []

**

更新为 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()

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

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

这是我在 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

如果你想播放提示音:

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)")
    }
}

Swift 4 版本:

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

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

您可以使用 this link 以获得最佳答案,如果您没有时间观看 10 分钟,最好的做法是您只需 copy 下面的代码AppDelegatedidFinishLaunchingWithOptions 然后 select 你的 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 {

    }
}

我认为仅仅将 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),但是当我离开应用程序时音频停止闪避,所以它适用于我的应用程序。

因为他们似乎无法在版本之间做出决定。这里是 Swift 5.0

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

Swift 5 版本基于 lchamp 的回答。

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

这对我来说非常适合 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")
    }
}

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

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

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