iOS: 如何在不掉帧的情况下播放音频?
iOS: How to play audio without fps drops?
我正在使用 Sprite Kit 并最好使用 Swift 库为 iOS 9+ 开发游戏。
目前我正在使用 Singleton 预加载我的音频文件,每个文件都连接到一个单独的 AVAudioPlayer 实例。
这里有一个简短的代码片段来理解这个想法:
import SpriteKit
import AudioToolbox
import AVFoundation
class AudioEngine {
static let sharedInstance = AudioEngine()
internal var sfxPing: AVAudioPlayer
private init() {
self.sfxPing = AVAudioPlayer()
if let path = NSBundle.mainBundle().pathForResource("ping", ofType: "m4a") {
do {
let url = NSURL(fileURLWithPath:path)
sfxPing = try AVAudioPlayer(contentsOfURL: url)
sfxPing.prepareToPlay()
} catch {
print("ERROR: Can't load ping.m4a audio file.")
}
}
}
}
此单例在应用程序启动期间初始化。在游戏循环中,我只需调用以下行来播放特定的音频文件:
AudioEngine.sharedInstance.sfxPing.play()
这基本上是可行的,但是我在 iPad Air 上播放文件并且帧率从 60.0 下降到 56.0 时总是出现故障。
有人知道如何解决 AVAudioPlayer 的这个性能问题吗?
我还留意了第 3 方库,即:
- AudioKit[看起来很有分量]
- ObjectAL [2013 年最后更新 ...]
- AVAudioEngine [基于AVAudioPlayer,同样的问题?]
要求:
- 播放很多非常短的样本(如击球、命中等)
- 播放一些电机效果(这样投球会很好)
- 循环播放一些背景音/环境音
- 没有令人讨厌的故障/帧率下降!
您能否根据我的要求推荐上述任何库或指出使用上述代码的问题?
更新:
播放短音:
self.runAction(SKAction.playSoundFileNamed("sfx.caf", waitForCompletion: false))
确实提高了帧率。 I exported the audio files with Audiacity to the .caf format (Apple's Core Audio Format). 但在教程中,它们使用“Signed 32-bit PCM
”编码导出,这导致我的音频播放受到干扰。使用任何其他编码选项(32-bit float
、U-Law
、A-Law
等)对我来说效果很好。
为什么使用caf
格式?因为它是未压缩的,因此与 m4a
等压缩格式相比,它以更少的 CPU 开销更快地加载到内存中。对于在短时间内播放很多的短音效,这是有道理的,并且对于消耗几千字节的短音频文件,磁盘使用不会受到太大影响。对于较大的音频文件,如环境音乐和背景音乐,使用压缩格式 (mp3
、m4a
) 显然是更好的选择。
根据您的问题,如果您为 iOS 9+ 开发游戏,您可以使用新的 iOS 9 库 SKAudioNode (official Apple doc):
var backgroundMusic: SKAudioNode!
例如,您可以将此添加到 didMoveToView()
:
if let musicURL = NSBundle.mainBundle().URLForResource("music", withExtension: "m4a") {
backgroundMusic = SKAudioNode(URL: musicURL)
addChild(backgroundMusic)
}
你也可以用来播放简单的效果:
let beep = SKAudioNode(fileNamed: "beep.wav")
beep.autoplayLooped = false
self.addChild(beep)
最后,如果你想改变音量:
beep.runAction(SKAction.changeVolumeTo(0.4, duration: 0))
更新:
我看到你更新了关于 AVAudioPlayer
和 SKAction
的问题。我已经为我的 iOS8+ 兼容游戏测试了它们。
关于AVAudioPlayer
,我个人使用我根据旧的SKTAudio.
制作的自定义库
我看到你的代码,关于 AVAudioPlayer init,我的代码是不同的,因为我使用:
@available(iOS 7.0, *)
public init(contentsOfURL url: NSURL, fileTypeHint utiString: String?)
我不知道 fileTypeHint
是否有所作为,所以试着告诉我你的测试。
您的代码的优点:
使用基于 AVAudioPlayer
的共享实例音频管理器,您可以控制音量,随时随地使用您的管理器,确保与 iOS8
兼容
关于您的代码的缺点:
每次播放一个声音又想播放另一个声音,以前的就坏了,尤其是你开过背景音乐。
如何解决?根据这个 SO post 没有问题似乎 AVFoundation
仅限于 4 个 AVAudioPlayer
属性实例化,所以你可以这样做:
- 1) backgroundMusicPlayer: AVAudioPlayer!
- 2) soundEffectPlayer1: AVAudioPlayer!
- 3) soundEffectPlayer2: AVAudioPlayer!
- 4) soundEffectPlayer3: AVAudioPlayer!
你可以构建一个方法来切换 3 个音效,看看是否被占用:
if player.playing
并使用下一个免费播放器。使用此解决方法,您的声音始终可以正确播放,甚至是背景音乐。
我正在使用 Sprite Kit 并最好使用 Swift 库为 iOS 9+ 开发游戏。
目前我正在使用 Singleton 预加载我的音频文件,每个文件都连接到一个单独的 AVAudioPlayer 实例。
这里有一个简短的代码片段来理解这个想法:
import SpriteKit
import AudioToolbox
import AVFoundation
class AudioEngine {
static let sharedInstance = AudioEngine()
internal var sfxPing: AVAudioPlayer
private init() {
self.sfxPing = AVAudioPlayer()
if let path = NSBundle.mainBundle().pathForResource("ping", ofType: "m4a") {
do {
let url = NSURL(fileURLWithPath:path)
sfxPing = try AVAudioPlayer(contentsOfURL: url)
sfxPing.prepareToPlay()
} catch {
print("ERROR: Can't load ping.m4a audio file.")
}
}
}
}
此单例在应用程序启动期间初始化。在游戏循环中,我只需调用以下行来播放特定的音频文件:
AudioEngine.sharedInstance.sfxPing.play()
这基本上是可行的,但是我在 iPad Air 上播放文件并且帧率从 60.0 下降到 56.0 时总是出现故障。
有人知道如何解决 AVAudioPlayer 的这个性能问题吗?
我还留意了第 3 方库,即:
- AudioKit[看起来很有分量]
- ObjectAL [2013 年最后更新 ...]
- AVAudioEngine [基于AVAudioPlayer,同样的问题?]
要求:
- 播放很多非常短的样本(如击球、命中等)
- 播放一些电机效果(这样投球会很好)
- 循环播放一些背景音/环境音
- 没有令人讨厌的故障/帧率下降!
您能否根据我的要求推荐上述任何库或指出使用上述代码的问题?
更新:
播放短音:
self.runAction(SKAction.playSoundFileNamed("sfx.caf", waitForCompletion: false))
确实提高了帧率。 I exported the audio files with Audiacity to the .caf format (Apple's Core Audio Format). 但在教程中,它们使用“Signed 32-bit PCM
”编码导出,这导致我的音频播放受到干扰。使用任何其他编码选项(32-bit float
、U-Law
、A-Law
等)对我来说效果很好。
为什么使用caf
格式?因为它是未压缩的,因此与 m4a
等压缩格式相比,它以更少的 CPU 开销更快地加载到内存中。对于在短时间内播放很多的短音效,这是有道理的,并且对于消耗几千字节的短音频文件,磁盘使用不会受到太大影响。对于较大的音频文件,如环境音乐和背景音乐,使用压缩格式 (mp3
、m4a
) 显然是更好的选择。
根据您的问题,如果您为 iOS 9+ 开发游戏,您可以使用新的 iOS 9 库 SKAudioNode (official Apple doc):
var backgroundMusic: SKAudioNode!
例如,您可以将此添加到 didMoveToView()
:
if let musicURL = NSBundle.mainBundle().URLForResource("music", withExtension: "m4a") {
backgroundMusic = SKAudioNode(URL: musicURL)
addChild(backgroundMusic)
}
你也可以用来播放简单的效果:
let beep = SKAudioNode(fileNamed: "beep.wav")
beep.autoplayLooped = false
self.addChild(beep)
最后,如果你想改变音量:
beep.runAction(SKAction.changeVolumeTo(0.4, duration: 0))
更新:
我看到你更新了关于 AVAudioPlayer
和 SKAction
的问题。我已经为我的 iOS8+ 兼容游戏测试了它们。
关于AVAudioPlayer
,我个人使用我根据旧的SKTAudio.
我看到你的代码,关于 AVAudioPlayer init,我的代码是不同的,因为我使用:
@available(iOS 7.0, *)
public init(contentsOfURL url: NSURL, fileTypeHint utiString: String?)
我不知道 fileTypeHint
是否有所作为,所以试着告诉我你的测试。
您的代码的优点:
使用基于 AVAudioPlayer
的共享实例音频管理器,您可以控制音量,随时随地使用您的管理器,确保与 iOS8
关于您的代码的缺点: 每次播放一个声音又想播放另一个声音,以前的就坏了,尤其是你开过背景音乐。
如何解决?根据这个 SO post 没有问题似乎 AVFoundation
仅限于 4 个 AVAudioPlayer
属性实例化,所以你可以这样做:
- 1) backgroundMusicPlayer: AVAudioPlayer!
- 2) soundEffectPlayer1: AVAudioPlayer!
- 3) soundEffectPlayer2: AVAudioPlayer!
- 4) soundEffectPlayer3: AVAudioPlayer!
你可以构建一个方法来切换 3 个音效,看看是否被占用:
if player.playing
并使用下一个免费播放器。使用此解决方法,您的声音始终可以正确播放,甚至是背景音乐。