第一次播放声音时 AVAudioPlayer 启动缓慢
-
23-08-2019 - |
题
我试图消除通过 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 对象并在内存不足的情况下积极清除它们?
解决方案
在延迟似乎与对实例化AVAudioPlayer首次。如果我加载任何音频,运行[audioPlayer prepareToPlay],然后立即释放它,我的所有其他音频的加载时间非常接近,难以察觉。所以,现在我在做,在的applicationDidFinishLaunching和其他一切运行良好。
我找不到这事的文档,但它肯定似乎是这样。
其他提示
这是我做了什么(在一个单独的线程):
[audioplayer start]
[audioplayer stop]
self.audioplayer = audioplayer
[audioplayer prepareToPlay]似乎是一个异步方法,所以你不能肯定,当它返回如果音频实际上是在准备比赛。
在我来说,我打电话开始真正开始播放 - 这似乎是同步的。然后,我立即停止。在模拟器反正我没有听到任何声音,从这个活动出来。现在的声音“真的”准备上场,我给你的局部变量的成员变量,因此线程之外的代码有访问它。
我必须说我觉得有些令人惊奇的是,即使在iOS 4它需要一些2秒只是加载的音频文件,这只是4秒长度....
如果您的音频是小于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);
这对我的作品,播放的声音很该死接近时,按下按钮。 希望这可以帮助你。
我已经采取了另一种方法为我的作品。我见过其他地方提到的(尽管我没有在时刻,这是召回)这种技术。
总之,通过加载预检该音响系统和打短,“空白”的声音,你做任何事情之前。的代码看起来是这样一个短的mp3文件我在视图controller'sviewDidLoad
方法预加载是0.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”谷歌搜索找到并下载一个已经由他人构成的。
我写AVAudioPlayer一个简单的包装提供该实际工作一个prepareToPlay方法:
https://github.com/nicklockwood/SoundManager
这基本上只是扫描您的声音文件的应用程序,挑选一个在零音量播放。这初始化音频硬件,并允许下一个声音即时播放。
其他解决方法是创建一个短而无声的音频并在第一次启动播放器时播放它。
- 在系统偏好设置中将麦克风级别设置为 0。
- 打开 QuickTime。
- 创建新的录音(文件 > 新录音)。
- 记录 1 或 2 秒。
- 保存/导出音频文件。(它的扩展名为 .m4a,大小约为 2 kb)。
- 将文件添加到您的项目并播放它。
如果您不想自己创建新的声音文件,您可以在此处下载简短且无声的音频文件: https://www.dropbox.com/s/3u45x9v72ic70tk/silent.m4a?dl=0
下面是一个简单的斯威夫特扩展AVAudioPlayer使用播放和停止在以前的答案给出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()
}
}
我不知道是肯定的,但我怀疑NSData对象是懒惰和加载按需文件的内容。您可以通过一些早期的点叫[audioData getBytes:someBuffer length:1];
获得真实需要之前它加载该文件试图“欺骗”。
答案是
AVAudioPlayer *audioplayer;
[audioplayer prepareToPlay];