如何在AVAudioPlayerNode中实现指定次数的音频播放?
实现AVAudioPlayerNode的固定次数循环播放
好问题!确实AVAudioPlayerNode没有提供像AVAudioPlayer那样直接的numberOfLoops属性来指定固定重复次数,但我们可以通过手动跟踪剩余循环次数+播放完成回调重新调度的方式来实现这个需求,逻辑其实很清晰。
核心思路
- 定义一个变量记录剩余需要重复的次数(和
AVAudioPlayer的numberOfLoops逻辑对齐:比如loopCount=0代表只播放1次,loopCount=2代表播放3次) - 第一次调度音频缓冲区进行播放
- 在播放完成的回调里,检查剩余循环次数:如果还有剩余,就减少计数并重新调度缓冲区,直到次数耗尽
完整代码示例
import AVFoundation class FixedLoopAudioPlayer: NSObject { private let audioEngine = AVAudioEngine() private let playerNode = AVAudioPlayerNode() private var remainingLoopCount: Int = 0 private var targetBuffer: AVAudioPCMBuffer? // 用于线程同步的串行队列,避免多线程操作计数时的竞态问题 private let playerQueue = DispatchQueue(label: "com.yourname.FixedLoopAudioPlayer.queue") override init() { super.init() setupAudioEngine() } private func setupAudioEngine() { // 挂载playerNode并连接到混音器 audioEngine.attach(playerNode) let mainMixer = audioEngine.mainMixerNode let outputFormat = mainMixer.outputFormat(forBus: 0) audioEngine.connect(playerNode, to: mainMixer, format: outputFormat) // 启动音频引擎 do { try audioEngine.start() } catch { print("音频引擎启动失败: \(error.localizedDescription)") } } /// 播放音频缓冲区,并指定重复次数 /// - Parameters: /// - buffer: 要播放的音频缓冲区 /// - loopCount: 重复次数,和AVAudioPlayer的numberOfLoops一致:0=播放1次,1=播放2次,以此类推 func play(buffer: AVAudioPCMBuffer, loopCount: Int) { playerQueue.sync { self.remainingLoopCount = loopCount self.targetBuffer = buffer // 首次调度播放 self.scheduleNextPlay() } } private func scheduleNextPlay() { guard let buffer = targetBuffer else { return } playerNode.scheduleBuffer(buffer, completionHandler: { [weak self] in guard let self = self else { return } self.playerQueue.sync { if self.remainingLoopCount > 0 { self.remainingLoopCount -= 1 self.scheduleNextPlay() } } }) // 如果playerNode未处于播放状态,启动播放 if !playerNode.isPlaying { playerNode.play() } } /// 停止播放并重置循环计数 func stop() { playerQueue.sync { playerNode.stop() remainingLoopCount = 0 targetBuffer = nil } } }
代码说明
- 线程安全:用串行队列
playerQueue来同步对remainingLoopCount和targetBuffer的操作,避免音频线程和主线程同时操作导致的竞态问题 - 逻辑对齐:
loopCount参数的行为和AVAudioPlayer的numberOfLoops完全一致,降低迁移成本 - 可扩展性:这个类可以很方便地扩展暂停、继续等功能
使用示例
// 假设你已经加载了AVAudioPCMBuffer到audioBuffer变量中 let player = FixedLoopAudioPlayer() // 播放3次(原播放+2次重复) player.play(buffer: audioBuffer, loopCount: 2) // 如需停止 // player.stop()
内容的提问来源于stack exchange,提问作者Simant
相关产品推荐
相关产品推荐

