You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AudioKit中completionHandler与schedule buffer使用问题:回调未触发

问题分析与解决方案

你的代码能正常播放PCM buffer但completionHandler始终不触发,核心问题是线程阻塞导致RunLoop无法处理回调,同时存在冗余调用干扰播放流程,具体修正如下:

核心问题:阻塞RunLoop

你在player.play()后用while循环+sleep(3)阻塞了当前线程,而AudioKit的completionHandler依赖RunLoop(或音频线程的回调调度)才能触发。线程被阻塞后,回调事件无法被处理,自然不会执行。

冗余调用干扰

你已经通过AudioPlayer(buffer: buffer1!)初始化了携带目标buffer的播放器,后续重复设置player.buffer = buffer1!和调用player.schedule(at:...)属于冗余操作,反而可能打乱播放器的内部状态。

修正后的代码

import AudioKit

let audioURL = URL(fileURLWithPath: "/Users/myuser/greeneggs.wav")
let audioFile = try! AVAudioFile(forReading: audioURL)
print("audioFile length is \(audioFile.length)")

let format = audioFile.processingFormat
let buffer1 = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(audioFile.length/8))!
audioFile.framePosition = 0
try audioFile.read(into: buffer1, frameCount: AVAudioFrameCount(audioFile.length/8))

let audioEngine = AudioEngine()
let player = AudioPlayer(buffer: buffer1)!

audioEngine.output = player
player.completionHandler = bufferCompletionHandler

do {
    try audioEngine.start()
    print("Audio engine started successfully.")
} catch {
    print("Error starting audio engine: \(error)")
}

player.play()

// 保持RunLoop运行,避免程序直接退出,同时让回调能被正常处理
RunLoop.main.run(until: Date().addingTimeInterval(player.duration + 1))

print("Done!")

func bufferCompletionHandler(){
    print("!!!!!!+++++...Scheduled...+++++!!!!!!")
}

关键修改说明

  • 移除冗余的player.buffer = buffer1!和player.schedule(at:...)调用,直接使用初始化时传入的buffer进行播放
  • 替换while循环+sleep为RunLoop.main.run(until:...),既保证程序运行到播放完成后再退出,又不会阻塞RunLoop,确保回调能被触发
  • 修正buffer1的强制解包(添加!),避免隐式解包的潜在风险

如果需要在播放过程中打印状态,建议使用定时器而非阻塞循环,示例如下:

// 播放开始后添加定时器,每隔3秒打印状态
let timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { timer in
    guard player.isPlaying else {
        timer.invalidate()
        return
    }
    print("Players gonna play")
    print("isBuffered \(player.isBuffered)")
    print("duration \(player.duration)")
    print("status \(player.status)")
    print("isLooping \(player.isLooping)")
}

内容的提问来源于stack exchange,提问作者John Brown

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 03:06:02