首页 文章

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

提问于
浏览
36

我试图通过 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 秒。然而,之后立即播放声音。我怀疑那个罪魁祸首是[3]最初从闪存中读取了很长时间,但后来读取速度很快。不过,我不确定如何测试。

是这样的吗?如果是这样,我应该只是 pre-cache NSData 对象并积极在低内存条件下清除它们?

9 回答

  • -1

    我不确定,但我怀疑 NSData 对象是懒惰的,并按需加载文件的内容。您可以通过在某个早期点调用[audioData getBytes:someBuffer length:1];来尝试“作弊”,以便在需要之前加载该文件。

  • -1

    答案是

    AVAudioPlayer *audioplayer;
    [audioplayer prepareToPlay];
    
  • 37

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

    我在文档中找不到任何相关内容,但看起来确实如此。

  • 11

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

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

    [4]似乎是一种异步方法,因此如果音频实际上已准备就绪,您无法确定它何时返回。

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

    我必须说我觉得有点令人惊讶的是,即使在 iOS 4 上加载一个长度只有 4 秒的音频文件也只需要 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

    我采取了另一种适合我的方法。我已经看到过其他地方提到的这种技术(我不记得那时那是)。

    简而言之,在您执行任何其他操作之前,通过加载和播放短暂的“空白”声音来预警音响系统。对于我在视图控制器的viewDidLoad方法中预加载的第二个持续时间为.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”上进行谷歌搜索,找到并下载已经由其他人构建的 mp3。

  • 3

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

    https://github.com/nicklockwood/SoundManager

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

  • 0

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

    • 在“系统偏好设置”中将麦克风级别设置为 0

    • 打开 QuickTime。

    • 创建新的音频录制(文件>新录音)。

    • 记录 1 或 2 秒。

    • 保存/导出音频文件。 (它的扩展名为.m4a,大小约为 2 kb)。

    • 将文件添加到项目中并播放。

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

  • 0

    这是 AVAudioPlayer 的简单 Swift 扩展,它使用前面答案中提供的 play-and-stop at 0 volume idea。 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()
        }
    }
    

相关问题