OpenAL vs AVAudioPlayer vs 其他播放声音的技术
OpenAL vs AVAudioPlayer vs other techniques for playing sounds
我知道 OpenAL 是一个快速库,但它不支持任何压缩音频格式,而且使用起来不是那么容易...
AVAudioPlayer 不是那么快,但支持多种文件格式,以及 mp3 等压缩格式。
还有一个 SKAction class 可以播放声音,还有 SystemSoundID...
我有几个问题:
way/player/technique 最好玩什么:
- 音效(同时有多个)?
- 有时可以在短时间延迟后重复的音效
- 循环播放的背景音乐
另外,使用未压缩的音频来制作音效是明智之举吗?我想这没关系,因为这些文件的体积很小吗?
我个人使用 ObjectAL。这很好,因为它利用了 OpenAL 和 AVAudioPlayer,但从您那里抽象出了很多复杂的部分。我的游戏有背景音乐、同时播放的大量声音,以及根据精灵速度增加音量、音调等的可循环声音。 ObjectAL 可以做到这一切。
ObjectAL 可以使用它的 OALSimpleAudio class 来播放简单的声音和音乐循环。或者你可以更深入地研究它并做更复杂的事情。
我专门为我的游戏创建了一个围绕 ObjectAL 的简单包装器,因此我可以进一步抽象它。
据我所知,未压缩的音频更好。您只需要确保预加载声音,这样您的游戏就不会在每次播放声音时都尝试拉取文件。
这个非常简单的 class 已经为我的多个项目提供了完美的服务,同时有很多声音 运行。我发现它比大惊小怪的 OpenAL 简单得多。它具有您要求的一切,预设置声音、多个并发播放、延迟后、背景循环。
是否使用压缩文件并不重要,因为您首先设置了它。
SKAudio.h
#import <Foundation/Foundation.h>
@import AVFoundation;
@interface SKAudio : NSObject
+(AVAudioPlayer*)setupRepeatingSound:(NSString*)file volume:(float)volume;
+(AVAudioPlayer*)setupSound:(NSString*)file volume:(float)volume;
+(void)playSound:(AVAudioPlayer*)player;
+(void)playSound:(AVAudioPlayer*)player afterDelay:(float)delaySeconds;
+(void)pauseSound:(AVAudioPlayer*)player;
@end
SKAudio.m
#import "SKAudio.h"
@implementation SKAudio
#pragma mark -
#pragma mark setup sound
// get a repeating sound
+(AVAudioPlayer*)setupRepeatingSound:(NSString*)file volume:(float)volume {
AVAudioPlayer *s = [self setupSound:file volume:volume];
s.numberOfLoops = -1;
return s;
}
// setup a sound
+(AVAudioPlayer*)setupSound:(NSString*)file volume:(float)volume{
NSError *error;
NSURL *url = [[NSBundle mainBundle] URLForResource:file withExtension:nil];
AVAudioPlayer *s = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
s.numberOfLoops = 0;
s.volume = volume;
[s prepareToPlay];
return s;
}
#pragma mark sound controls
// play a sound now through GCD
+(void)playSound:(AVAudioPlayer*)player {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[player play];
});
}
// play a sound later through GCD
+(void)playSound:(AVAudioPlayer*)player afterDelay:(float)delaySeconds {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delaySeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[player play];
});
}
// pause a currently running sound (mostly for background music)
+(void)pauseSound:(AVAudioPlayer*)player {
[player pause];
}
@end
在你的游戏中使用:
设置一个 class 变量并用您的声音预加载它:
static AVAudioPlayer *whooshHit;
static AVAudioPlayer *bgMusic;
+(void)preloadShared {
// cache all the sounds in this class
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
whooshHit = [SKAudio setupSound:@"whoosh-hit-chime-1.mp3" volume:1.0];
// setup background sound with a lower volume
bgMusic = [SKAudio setupRepeatingSound:@"background.mp3" volume:0.3];
});
}
...
// whoosh with delay
[SKAudio playSound:whooshHit afterDelay:1.0];
...
// whoosh and shrink SKAction
SKAction *whooshAndShrink = [SKAction group:@[
[SKAction runBlock:^{ [SKAudio playStarSound:whooshHit afterDelay:1.0]; }],
[SKAction scaleTo:0 duration:1.0]]];
...
[SKAudio playSound:bgMusic];
...
[SKAudio pauseSound:bgMusic];
这是@patrick 对 Swift 3.
的解决方案的一部分
import AVFoundation
// MARK -
// MARK setup sound
// get a repeating sound
func setupRepeatingSound(file: String, volume: Float) -> AVAudioPlayer? {
let sound: AVAudioPlayer? = setupSound(file: file, volume: volume)
sound?.numberOfLoops = -1
return sound
}
// setup a sound
func setupSound(file: String, volume: Float) -> AVAudioPlayer? {
var sound: AVAudioPlayer?
if let path = Bundle.main.path(forResource: file, ofType:nil) {
let url = NSURL(fileURLWithPath: path)
do {
sound = try AVAudioPlayer(contentsOf: url as URL)
} catch {
// couldn't load file :(
}
}
sound?.numberOfLoops = 0
sound?.volume = volume
sound?.prepareToPlay()
return sound
}
// MARK sound controls
// play a sound now through GCD
func playSound(_ sound: AVAudioPlayer?) {
if sound != nil {
DispatchQueue.global(qos: .default).async {
sound!.play()
}
}
}
// play a sound later through GCD
func playSound(_ sound: AVAudioPlayer?, afterDelay: Float) {
if sound != nil {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(afterDelay)) {
sound!.play()
}
}
}
// pause a currently running sound (mostly for background music)
func pauseSound(_ sound: AVAudioPlayer?) {
sound?.pause()
}
我知道 OpenAL 是一个快速库,但它不支持任何压缩音频格式,而且使用起来不是那么容易...
AVAudioPlayer 不是那么快,但支持多种文件格式,以及 mp3 等压缩格式。
还有一个 SKAction class 可以播放声音,还有 SystemSoundID...
我有几个问题:
way/player/technique 最好玩什么:
- 音效(同时有多个)?
- 有时可以在短时间延迟后重复的音效
- 循环播放的背景音乐
另外,使用未压缩的音频来制作音效是明智之举吗?我想这没关系,因为这些文件的体积很小吗?
我个人使用 ObjectAL。这很好,因为它利用了 OpenAL 和 AVAudioPlayer,但从您那里抽象出了很多复杂的部分。我的游戏有背景音乐、同时播放的大量声音,以及根据精灵速度增加音量、音调等的可循环声音。 ObjectAL 可以做到这一切。
ObjectAL 可以使用它的 OALSimpleAudio class 来播放简单的声音和音乐循环。或者你可以更深入地研究它并做更复杂的事情。
我专门为我的游戏创建了一个围绕 ObjectAL 的简单包装器,因此我可以进一步抽象它。
据我所知,未压缩的音频更好。您只需要确保预加载声音,这样您的游戏就不会在每次播放声音时都尝试拉取文件。
这个非常简单的 class 已经为我的多个项目提供了完美的服务,同时有很多声音 运行。我发现它比大惊小怪的 OpenAL 简单得多。它具有您要求的一切,预设置声音、多个并发播放、延迟后、背景循环。
是否使用压缩文件并不重要,因为您首先设置了它。
SKAudio.h
#import <Foundation/Foundation.h>
@import AVFoundation;
@interface SKAudio : NSObject
+(AVAudioPlayer*)setupRepeatingSound:(NSString*)file volume:(float)volume;
+(AVAudioPlayer*)setupSound:(NSString*)file volume:(float)volume;
+(void)playSound:(AVAudioPlayer*)player;
+(void)playSound:(AVAudioPlayer*)player afterDelay:(float)delaySeconds;
+(void)pauseSound:(AVAudioPlayer*)player;
@end
SKAudio.m
#import "SKAudio.h"
@implementation SKAudio
#pragma mark -
#pragma mark setup sound
// get a repeating sound
+(AVAudioPlayer*)setupRepeatingSound:(NSString*)file volume:(float)volume {
AVAudioPlayer *s = [self setupSound:file volume:volume];
s.numberOfLoops = -1;
return s;
}
// setup a sound
+(AVAudioPlayer*)setupSound:(NSString*)file volume:(float)volume{
NSError *error;
NSURL *url = [[NSBundle mainBundle] URLForResource:file withExtension:nil];
AVAudioPlayer *s = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
s.numberOfLoops = 0;
s.volume = volume;
[s prepareToPlay];
return s;
}
#pragma mark sound controls
// play a sound now through GCD
+(void)playSound:(AVAudioPlayer*)player {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[player play];
});
}
// play a sound later through GCD
+(void)playSound:(AVAudioPlayer*)player afterDelay:(float)delaySeconds {
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delaySeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[player play];
});
}
// pause a currently running sound (mostly for background music)
+(void)pauseSound:(AVAudioPlayer*)player {
[player pause];
}
@end
在你的游戏中使用:
设置一个 class 变量并用您的声音预加载它:
static AVAudioPlayer *whooshHit;
static AVAudioPlayer *bgMusic;
+(void)preloadShared {
// cache all the sounds in this class
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
whooshHit = [SKAudio setupSound:@"whoosh-hit-chime-1.mp3" volume:1.0];
// setup background sound with a lower volume
bgMusic = [SKAudio setupRepeatingSound:@"background.mp3" volume:0.3];
});
}
...
// whoosh with delay
[SKAudio playSound:whooshHit afterDelay:1.0];
...
// whoosh and shrink SKAction
SKAction *whooshAndShrink = [SKAction group:@[
[SKAction runBlock:^{ [SKAudio playStarSound:whooshHit afterDelay:1.0]; }],
[SKAction scaleTo:0 duration:1.0]]];
...
[SKAudio playSound:bgMusic];
...
[SKAudio pauseSound:bgMusic];
这是@patrick 对 Swift 3.
的解决方案的一部分import AVFoundation
// MARK -
// MARK setup sound
// get a repeating sound
func setupRepeatingSound(file: String, volume: Float) -> AVAudioPlayer? {
let sound: AVAudioPlayer? = setupSound(file: file, volume: volume)
sound?.numberOfLoops = -1
return sound
}
// setup a sound
func setupSound(file: String, volume: Float) -> AVAudioPlayer? {
var sound: AVAudioPlayer?
if let path = Bundle.main.path(forResource: file, ofType:nil) {
let url = NSURL(fileURLWithPath: path)
do {
sound = try AVAudioPlayer(contentsOf: url as URL)
} catch {
// couldn't load file :(
}
}
sound?.numberOfLoops = 0
sound?.volume = volume
sound?.prepareToPlay()
return sound
}
// MARK sound controls
// play a sound now through GCD
func playSound(_ sound: AVAudioPlayer?) {
if sound != nil {
DispatchQueue.global(qos: .default).async {
sound!.play()
}
}
}
// play a sound later through GCD
func playSound(_ sound: AVAudioPlayer?, afterDelay: Float) {
if sound != nil {
DispatchQueue.main.asyncAfter(deadline: .now() + Double(afterDelay)) {
sound!.play()
}
}
}
// pause a currently running sound (mostly for background music)
func pauseSound(_ sound: AVAudioPlayer?) {
sound?.pause()
}