第一次播放声音时,AVAudioPlayer启动缓慢

我试图通过iPhone上的AVAudioPlayer播放(非常短 – less于2秒)的audio文件来消除启动延迟。

首先,代码:

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。

我第一次播放audio时,延迟是可以触摸的 – 至less2秒。 然而,之后,声音立即播放。 我怀疑罪魁祸首是最初从闪存中读取很长一段时间的[NSData dataWithContentsOfMappedFile],但后来读取的速度很快。 不过,我不确定如何testing。

是这样吗? 如果是的话,我应该只是预先caching的NSData对象,积极清理它们在低内存条件?

这个延迟似乎与第一次实例化AVAudioPlayer有关。 如果我加载任何audio,运行[audioPlayer prepareToPlay],然后立即释放它,所有其他audio的加载时间是非常接近不可察觉的。 所以现在我在applicationDidFinishLaunching中做了这一切,一切运行良好。

在文档中我找不到任何关于这个的内容,但似乎确实如此。

这是我所做的(在一个单独的线程中):

 [audioplayer start] [audioplayer stop] self.audioplayer = audioplayer 

[audioplayer prepareToPlay]似乎是一个asynchronous方法,所以如果audio实际上准备好播放,你不能确定它何时返回。

在我的情况下,我打电话开始实际开始播放 – 这似乎是同步的。 然后我马上停下来 无论如何,在模拟器中,我听不到这个活动发出的任何声音。 现在声音已经“准备就绪”了,我将局部variables赋值给一个成员variables,这样线程之外的代码就可以访问它了。

我必须说,我觉得有点令人惊讶的是,即使在iOS 4上,只需要2秒钟就可以加载长度仅为4秒的audio文件….

如果您的audio长度小于30秒,并且采用线性PCM或IMA4格式,并且打包为.caf,.wav或.aiff,则可以使用系统声音:

导入AudioToolbox框架

在你的.h文件中创build这个variables:

 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); 

这适用于我,播放声音相当接近当button按下。 希望这可以帮助你。

我为AVAudioPlayer写了一个简单的包装器,它提供了一个实际工作的prepareToPlay方法:

https://github.com/nicklockwood/SoundManager

它基本上只是扫描你的应用程序的声音文件,挑一个和零音量播放。 这初始化audio硬件,并允许立即播放下一个声音。

我采取了一种替代方法,适合我。 我已经看到了其他地方提到的这种技术(我现在不记得那是什么时候)。

总之,在你做任何事情之前,通过加载和播放一个简短的“空白”声音来预先检查音响系统。 代码看起来像这样一个短的MP3文件,我preload在我的视图控制器的viewDidLoad方法.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]; 

您可以创build自己的空白mp3,或者在“空白的mp3”上search谷歌,以查找和下载其他人已经构build的mp3。

其他的解决方法是创build一个短暂的和无声的audio,并在玩家第一次启动时播放。

  1. 在“系统预置”中将麦克风级别设置为0。
  2. 打开QuickTime。
  3. 创build新的audio录制(文件>新buildaudio录制)。
  4. logging1或2秒。
  5. 保存/导出audio文件。 (它将有.m4a扩展名,大小约2kb)。
  6. 将文件添加到您的项目并播放它。

如果你不想自己创build一个新的声音文件,你可以在这里下载一个简短和无声的audio文件: https : //www.dropbox.com/s/3u45x9v72ic70tk/silent.m4a?dl=0

以下是对AVAudioPlayer的一个简单的Swift扩展,它使用前面答案中提出的0音量播放和停止的思想。 PrepareToPlay()不幸的是,至less对我来说没有办法。

 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];来尝试“作弊” [audioData getBytes:someBuffer length:1]; 在一些早期的时候让它在需要之前加载该文件。

答案是

 AVAudioPlayer *audioplayer; [audioplayer prepareToPlay];