我试图通过iPhone上的AVAudioPlayer播放(非常短 - 不到2秒)音频文件时消除启动延迟。
首先,代码:
NSString *audioFile = [NSString stringWithFormat:@"%@/%@.caf", [[NSBundle mainBundle] resourcePath], @"audiofile"];
NSData *audioData = [NSData dataWithContentsOfMappedFile:audioFile];
NSError *err;
AVAudioPlayer *audioPlayer = [(AVAudioPlayer*)[AVAudioPlayer alloc] initWithData:audioData error:&err];
audioPlayer.delegate = self;
[audioPlayer play];
一旦完成,我还实现了audioPlayerDidFinishPlaying方法来释放AVAudioPlayer。
第一次播放音频时,滞后可触及 - 至少2秒。然而,之后立即播放声音。我怀疑那个罪魁祸首是[NSData dataWithContentsOfMappedFile]最初从闪存中读取了很长时间,但后来读取速度很快。不过,我不知道如何测试。
是这样的吗?如果是这样,我是否应该预先缓存NSData对象并积极地在低内存条件下清除它们?
答案 0 :(得分:37)
延迟似乎与第一次实例化AVAudioPlayer有关。如果我加载任何音频,运行[audioPlayer prepareToPlay]然后立即释放它,我所有其他音频的加载时间非常接近不可察觉。所以现在我在applicationDidFinishLaunching中这样做,其他一切运行良好。
我在文档中找不到任何关于此的内容,但看起来确实如此。
答案 1 :(得分:11)
这就是我所做的(在一个单独的主题中):
[audioplayer start]
[audioplayer stop]
self.audioplayer = audioplayer
[audioplayer prepareToPlay]似乎是一个异步方法,因此如果音频实际上已准备就绪,您无法确定它何时返回。
在我的情况下,我打电话开始实际开始播放 - 这似乎是同步的。然后我立即停止它。无论如何,在模拟器中,我听不到此活动发出的任何声音。现在声音“真的”准备播放了,我将局部变量分配给成员变量,因此线程外的代码可以访问它。
我必须说我觉得有点令人惊讶的是,即使在iOS 4上加载一个长度仅为4秒的音频文件也需要2秒钟。
答案 2 :(得分:8)
如果你的音频长度不到30秒,并且是线性PCM或IMA4格式,并打包为.caf,.wav或.aiff,你可以使用系统声音:
导入AudioToolbox框架
在.h文件中创建此变量:
SystemSoundID mySound;
在.m文件中,在init方法中实现它:
-(id)init{
if (self) {
//Get path of VICTORY.WAV <-- the sound file in your bundle
NSString* soundPath = [[NSBundle mainBundle] pathForResource:@"VICTORY" ofType:@"WAV"];
//If the file is in the bundle
if (soundPath) {
//Create a file URL with this path
NSURL* soundURL = [NSURL fileURLWithPath:soundPath];
//Register sound file located at that URL as a system sound
OSStatus err = AudioServicesCreateSystemSoundID((CFURLRef)soundURL, &mySound);
if (err != kAudioServicesNoError) {
NSLog(@"Could not load %@, error code: %ld", soundURL, err);
}
}
}
return self;
}
在您的IBAction方法中,您可以使用以下方法调用声音:
AudioServicesPlaySystemSound(mySound);
这适合我,播放按下按钮时非常接近的声音。 希望这会对你有所帮助。
答案 3 :(得分:3)
我为AVAudioPlayer编写了一个简单的包装器,它提供了一个实际工作的prepareToPlay方法:
https://github.com/nicklockwood/SoundManager
它基本上只扫描你的应用程序的声音文件,选择一个并以零音量播放它。这会初始化音频硬件并允许下一个声音立即播放。
答案 4 :(得分:2)
我采取了另一种适合我的方法。我已经看到过其他地方提到的这种技术(我不记得当时的情况)。
简而言之,在您执行任何其他操作之前,通过加载并播放短暂的“空白”声音来预警音响系统。对于我在视图控制器的viewDidLoad
方法中预加载的短mp3文件,代码看起来像这样。持续时间为.1秒
NSError* error = nil;
NSString* soundfilePath = [[NSBundle mainBundle] pathForResource:@"point1sec" ofType:@"mp3"];
NSURL* soundfileURL = [NSURL fileURLWithPath:soundfilePath];
AVAudioPlayer* player = [[[AVAudioPlayer alloc] initWithContentsOfURL:soundfileURL error:&error] autorelease];
[player play];
如果你愿意,你可以创建自己的空白mp3,或者在“空白mp3”上进行谷歌搜索,找到并下载已经由其他人构建的mp3。
答案 5 :(得分:0)
其他解决方法是创建一个简短的&amp;静音,并在第一次启动播放器时播放。
如果你不想自己创建一个新的声音文件,你可以下载一个简短的&amp;这里的无声音频文件:https://www.dropbox.com/s/3u45x9v72ic70tk/silent.m4a?dl=0
答案 6 :(得分:0)
这是AVAudioPlayer的一个简单的Swift扩展,它使用前面答案中提到的0音量的播放和停止。不幸的是,PrepareToPlay()至少对我来说没有做到这一点。
extension AVAudioPlayer {
private func initDelaylessPlayback() {
volume = 0
play()
stop()
volume = 1
}
convenience init(contentsOfWithoutDelay : URL) throws {
try self.init(contentsOf: contentsOfWithoutDelay, fileTypeHint: nil)
initDelaylessPlayback()
}
}
答案 7 :(得分:-1)
我不确定,但我怀疑NSData对象是懒惰的,并按需加载文件的内容。您可以在某个早期点调[audioData getBytes:someBuffer length:1];
来尝试“作弊”,以便在需要之前加载该文件。
答案 8 :(得分:-1)
答案是
AVAudioPlayer *audioplayer;
[audioplayer prepareToPlay];