40

我试图通过 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 对象并在内存不足的情况下积极清除它们?

4

9 回答 9

39

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

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

于 2010-06-21T04:48:51.700 回答
11

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

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

[audioplayer prepareToPlay] 似乎是一个异步方法,因此如果音频实际上已准备好播放,您无法确定它何时返回。

在我的情况下,我调用 start 来实际开始播放 - 这似乎是同步的。然后我立即停止。无论如何,在模拟器中,我没有听到此活动发出任何声音。现在声音“真的”可以播放了,我将局部变量分配给一个成员变量,以便线程外的代码可以访问它。

我必须说,即使在 iOS 4 上,加载一个长度仅为 4 秒的音频文件也需要大约 2 秒,这让我感到有些惊讶......

于 2011-03-15T01:14:22.087 回答
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);

这对我有用,在按下按钮时播放的声音非常接近。希望这对您有所帮助。

于 2012-01-03T04:55:23.677 回答
4

我采取了另一种适合我的方法。我在其他地方看到过这种技术(但我现在不记得那是在哪里)。

简而言之,在执行任何其他操作之前,通过加载并播放一个简短的“空白”声音来预检音响系统。对于我在视图控制器的viewDidLoad方法中预加载的持续时间为 0.1 秒的简短 mp3 文件,代码如下所示:

   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”上进行谷歌搜索以查找并下载其他人已经构建的。

于 2011-10-06T16:52:54.103 回答
3

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

https://github.com/nicklockwood/SoundManager

它基本上只是扫描您的应用程序中的声音文件,选择一个并以零音量播放。这会初始化音频硬件并允许立即播放下一个声音。

于 2012-05-02T16:21:27.493 回答
2

这是 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()
    }
}
于 2017-08-14T07:59:16.577 回答
0

其他解决方法是创建一个短而无声的音频,并在第一次启动播放器时播放。

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

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

于 2016-11-25T23:33:53.823 回答
-1

我不确定,但我怀疑 NSData 对象很懒惰并按需加载文件的内容。您可以尝试“作弊”,[audioData getBytes:someBuffer length:1];方法是尽早调用以使其在需要之前加载该文件。

于 2009-05-23T00:36:32.793 回答
-1

答案是

AVAudioPlayer *audioplayer;
[audioplayer prepareToPlay];
于 2010-07-18T23:45:37.233 回答