PAYATHON 2026

SwiftUI 中 AVAudioPlayer 播放音频为何逐个变慢?

数据小陈

结论

现有信息不足以确定具体原因。点击 “A” 到 “Z” 时,延迟逐渐增加。可以先检查对应 MP3 文件开头的静音是否也逐渐变长。如果有前置静音,AVAudioPlayer 会从文件开头播放,因此听到声音的时间会相应推迟。如果文件开头没有静音,再检查播放器是否已准备好,以及按钮回调中是否有耗时操作。

你已尝试在 .task 中创建播放器,但没有调用 prepareToPlay()。另一个 Sounds 示例则是在点击时重新创建播放器。这两种做法都不足以单独证明延迟是由播放器初始化造成的。

先检查音频文件

在音频编辑器中查看每个 MP3 的波形,或逐个播放文件开头,确认第一个有效声音出现前是否有静音。也可以裁掉每个文件开头的部分,重新导出后比较播放效果。MP3 编码可能会带来很短的编码延迟;如果空白有数秒,应先检查文件内容和资源映射。

同时确认 character.file 指向的是预期资源。可以在点击时打印文件名,并在 play() 前后记录时间,以判断延迟发生在调用播放之前,还是文件开始播放之后:

private func keypressed() {
    print("播放资源:\(character.file)")
    let startedAt = Date()

    guard let soundURL = Bundle.main.url(
        forResource: character.file,
        withExtension: "mp3"
    ) else {
        print("找不到音频资源:\(character.file)")
        return
    }

    do {
        player = try AVAudioPlayer(contentsOf: soundURL)
        player?.prepareToPlay()
        let didStart = player?.play() ?? false
        print("play() 返回:\(didStart),耗时:\(Date().timeIntervalSince(startedAt)) 秒")
    } catch {
        print("Failed to load the sound: \(error)")
    }
}

play() 返回 true 只表示播放器已安排播放,不能确认扬声器已经输出可听见的声音。要准确比较实际出声时间,可以用录音或音频分析工具观察输出。

预先创建并准备播放器

如果文件没有前置静音,可以在视图出现时创建播放器并调用 prepareToPlay()。点击时只需触发播放,并将播放位置重置到文件开头:

.task {
    guard let soundURL = Bundle.main.url(
        forResource: character.file,
        withExtension: "mp3"
    ) else {
        return
    }

    do {
        let audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
        audioPlayer.prepareToPlay()
        player = audioPlayer
    } catch {
        print("Failed to load the sound: \(error)")
    }
}

private func keypressed() {
    onTap(character)
    player?.currentTime = 0
    let didStart = player?.play() ?? false
    print("play() 返回:\(didStart)")
}

播放期间要确保播放器仍被强引用。上例用 @State 保存播放器,通常可以满足这一点。如果把播放器放在管理类中,也要确保管理类持有播放器实例,避免播放器在播放后立即释放。

若问题仍存在

在 onTap(character) 前后记录时间,检查回调是否执行了耗时操作。也可以暂时注释这一行进行测试。如果 play() 很快返回,但实际出声仍有延迟,重点检查 MP3 前置静音、资源对应关系和音频会话配置。如果延迟发生在调用 play() 之前,则检查文件加载或回调中的同步工作。

备注:内容仅供参考。