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

Posted

技术标签:

【中文标题】如何让背景音乐继续播放而我的应用程序在使用 Swift 时仍然播放其声音【英文标题】:How can I allow background music to continue playing while my app still plays its sounds while using 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()
    

【问题讨论】:

【参考方案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)

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

【讨论】:

以上是关于如何让背景音乐继续播放而我的应用程序在使用 Swift 时仍然播放其声音的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中,如何让其他应用在我的应用打开时继续播放音频?

打开文件时使我的程序成为“选择默认程序”

如何使用 expo-av 在本机反应中播放背景音频?

如何将音乐从我的应用程序切换到 ipod

如何在屏幕关闭背景可播放选项的情况下播放 android 收音机

当用户浏览网站时,我将如何在网站上继续播放音乐?