应用程序被iOS通话中断后,音频将无法播放 [英] Audio won't play after app interrupted by phone call iOS

查看:112
本文介绍了应用程序被iOS通话中断后,音频将无法播放的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的SpriteKit游戏出现问题,该应用程序被电话中断后,无法使用playSoundFileNamed(_ soundFile :, waitForCompletion :)播放音频. (我还在我的应用程序中使用了未受影响的SKAudioNodes,但我确实非常希望能够使用SKAction playSoundFileNamed.)

I have a problem in my SpriteKit game where audio using playSoundFileNamed(_ soundFile:, waitForCompletion:) will not play after the app is interrupted by a phone call. (I also use SKAudioNodes in my app which aren't affected but I really really really want to be able to use the SKAction playSoundFileNamed as well.)

这是精简版SpriteKit游戏模板中的gameScene.swift文件,它再现了该问题.您只需将音频文件添加到项目中,并将其命名为"note"

Here's the gameScene.swift file from a stripped down SpriteKit game template which reproduces the problem. You just need to add an audio file to the project and call it "note"

我已经将应驻留在appDelegate中的代码附加到开/关切换按钮上,以模拟电话中断.该代码1)停止AudioEngine,然后停用AVAudioSession-(通常在applicationWillResignActive中)...,2)激活AVAudioSession,然后启动AudioEngine-(通常在applicationDidBecomeActive中)

I've attached the code that should reside in appDelegate to a toggle on/off button to simulate the phone call interruption. That code 1) Stops AudioEngine then deactivates AVAudioSession - (normally in applicationWillResignActive) ... and 2) Activates AVAudioSession then Starts AudioEngine - (normally in applicationDidBecomeActive)

错误:

AVAudioSession.mm:1079:-[AVAudioSession setActive:withOptions:error:]:停用正在运行I/O的音频会话.在停用音频会话之前,应停止或暂停所有I/O.

AVAudioSession.mm:1079:-[AVAudioSession setActive:withOptions:error:]: Deactivating an audio session that has running I/O. All I/O should be stopped or paused prior to deactivating the audio session.

在尝试停用音频会话时会发生这种情况,但仅在播放声音至少一次之后才发生. 再现:

This occurs when attempting to deactivate the audio session but only after a sound has been played at least once. to reproduce:

1)运行应用 2)将引擎关闭然后再打开几次.不会发生错误. 3)点击playSoundFileNamed按钮1次或更多次以播放声音. 4)等待声音停止 5)再确认一下

1) Run the app 2) toggle the engine off and on a few times. No error will occur. 3) Tap the playSoundFileNamed button 1 or more times to play the sound. 4) Wait for sound to stop 5) Wait some more to be sure

6)点击切换音频引擎按钮以停止audioEngine并停用会话- 错误发生.

6) Tap Toggle Audio Engine button to stop the audioEngine and deactivate session - the error occurs.

7)切换引擎几次,以查看调试区域中已打印的会话已激活,会话已禁用,会话已激活-即未报告任何错误. 8)现在,在会话处于活动状态且引擎正在运行的情况下,playSoundFileNamed按钮将不再播放声音.

7) Toggle the engine on and of a few times to see session activated, session deactivated, session activated printed in debug area - i.e. no errors reported. 8) Now with session active and engine running, playSoundFileNamed button will not play the sound anymore.

我在做什么错了?

import SpriteKit
import AVFoundation

class GameScene: SKScene {
var toggleAudioButton: SKLabelNode?
var playSoundFileButton: SKLabelNode?
var engineIsRunning = true

override func didMove(to view: SKView) {
toggleAudioButton = SKLabelNode(text: "toggle Audio Engine")
toggleAudioButton?.position = CGPoint(x:20, y:100)
toggleAudioButton?.name = "toggleAudioEngine"
toggleAudioButton?.fontSize = 80
addChild(toggleAudioButton!)

playSoundFileButton = SKLabelNode(text: "playSoundFileNamed")
playSoundFileButton?.position = CGPoint(x: (toggleAudioButton?.frame.midX)!, y:    (toggleAudioButton?.frame.midY)!-240)
playSoundFileButton?.name = "playSoundFileNamed"
playSoundFileButton?.fontSize = 80
addChild(playSoundFileButton!)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
  let  location = touch.location(in: self)
  let  nodes = self.nodes(at: location)

  for spriteNode in nodes {
    if spriteNode.name == "toggleAudioEngine" {
      if engineIsRunning { // 1 stop engine, 2 deactivate session

        scene?.audioEngine.stop() // 1
        toggleAudioButton!.text = "engine is paused"
        engineIsRunning = !engineIsRunning
        do{
          // this is the line that fails when hit anytime after the playSoundFileButton has played a sound
          try AVAudioSession.sharedInstance().setActive(false) // 2
          print("session deactivated")
        }
        catch{
          print("DEACTIVATE SESSION FAILED")
        }
      }
      else { // 1 activate session/ 2 start engine
        do{
          try AVAudioSession.sharedInstance().setActive(true) // 1
          print("session activated")
        }
        catch{
          print("couldn't setActive = true")
        }
        do {
          try scene?.audioEngine.start() // 2
          toggleAudioButton!.text = "engine is running"
          engineIsRunning = !engineIsRunning
        }
        catch {
          //
        }
      }
    }

    if spriteNode.name == "playSoundFileNamed" {
      self.run(SKAction.playSoundFileNamed("note", waitForCompletion: false))
    }
   }
  }
 }
}

推荐答案

让我在这里为您节省一些时间:playSoundFileNamed在理论上听起来很棒,您可以说在您花了4年时间开发直到一天的应用程序中使用了它.您意识到它不仅在中断方面完全崩溃,而且甚至在最关键的中断(IAP)中使您的应用程序崩溃.不要做我仍然不确定是要用SKAudioNode还是AVPlayer来解决问题,但这可能取决于您的用例.只是不要这样做.

Let me save you some time here: playSoundFileNamed sounds wonderful in theory, so wonderful that you might say use it in an app you spent 4 years developing until one day you realize it’s not just totally broken on interruptions but will even crash your app in the most critical of interruptions, your IAP. Don’t do it. I’m still not entirely sure whether SKAudioNode or AVPlayer is the answer, but it may depend on your use case. Just don’t do it.

如果您需要科学依据,请创建一个应用程序并创建一个for循环,该循环可以在touchesBegan中随意播放playSoundFileNamed,并查看内存使用情况.该方法是一块泄漏的垃圾.

If you need scientific evidence, create an app and create a for loop that playSoundFileNamed whatever you want in touchesBegan, and see what happens to your memory usage. The method is a leaky piece of garbage.

针对我们的最终解决方案进行了

EDITED FOR OUR FINAL SOLUTION:

我们发现,使用prepareToPlay()在内存中具有适当数量的AVAudioPlayer的预加载实例是最好的方法. SwiftySound音频类使用即时生成器,但使AVAudioPlayers即时运行会降低动画的速度.我们发现有最大数量的AVAudioPlayers并检查一个数组,其中isPlaying == false最简单也是最好的;如果没有声音,您将不会听到声音,类似于PSFN彼此叠加播放大量声音时可能会看到的声音.总体而言,我们还没有找到理想的解决方案,但这对我们来说已经很接近了.

We found having a proper number of preloaded instances of AVAudioPlayer in memory with prepareToPlay() was the best method. The SwiftySound audio class uses an on-the-fly generator, but making AVAudioPlayers on the fly created slowdown in animation. We found having a max number of AVAudioPlayers and checking an array for those where isPlaying == false was simplest and best; if one isn't available you don't get sound, similar to what you likely saw with PSFN if you had it playing lots of sounds on top of each other. Overall, we have not found an ideal solution, but this was close for us.

这篇关于应用程序被iOS通话中断后,音频将无法播放的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆