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

时间:2009-05-22 23:50:47

标签: iphone caching audio nsdata avaudioplayer

我试图通过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对象并积极地在低内存条件下清除它们?

9 个答案:

答案 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;静音,并在第一次启动播放器时播放。

  1. 在“系统偏好设置”中将麦克风级别设置为0.
  2. 打开QuickTime。
  3. 创建新的音频录制(文件&gt;新录音)。
  4. 记录1或2秒。
  5. 保存/导出音频文件。 (它的扩展名为.m4a,大小约为2 kb。)
  6. 将文件添加到项目中并播放。
  7. 如果你不想自己创建一个新的声音文件,你可以下载一个简短的&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];