如何为AVAudioPCMBuffer设置播放偏移实现AVAudioPlayerNode进度跳转
AVAudioPCMBuffer 播放偏移设置实现方案
AVAudioPCMBuffer 本身没有原生offset属性,你可以通过两种常用方案实现指定位置播放:
- 方案一:使用AVAudioPlayerNode原生段调度API(推荐,无额外内存开销)
直接调用AVAudioPlayerNode.scheduleSegment(_:startingFrame:count:at:options:completionHandler:)调度原buffer的指定区间即可,不需要修改原buffer内容:
internal func seek(to offsetSec: Double) { guard let buf = pcmBuf else { logger?.log(severity: .error, msg: "Sound pcmBuf is nil.") return } // 停止当前播放,清空已有调度任务 player.stop() isPlaying = false let sampleRate = buf.format.sampleRate let startFrame = AVAudioFramePosition(offsetSec * sampleRate) // 边界校验:避免偏移超过buffer总时长 guard startFrame < buf.frameLength else { // 可按需处理超出范围的场景,比如直接结束播放 return } let playFrameCount = AVAudioFrameCount(buf.frameLength - startFrame) // 调度指定区间,保留原循环逻辑 player.scheduleSegment(buf, startingFrame: startFrame, count: playFrameCount, at: nil, options: .loops) player.play() isPlaying = true }
该方案优势是不需要做内存拷贝,性能最优,设置.loops参数后会循环播放你指定的区间段,符合原有播放逻辑。
- 方案二:手动生成偏移后的子Buffer(适合需要复用偏移后buffer的场景)
如果你需要把偏移后的内容单独保存复用,可以手动将原buffer中偏移位置之后的数据拷贝到新的PCMBuffer中,再调度新buffer:
internal func makeOffsetBuffer(from original: AVAudioPCMBuffer, offsetSec: Double) -> AVAudioPCMBuffer? { let sampleRate = original.format.sampleRate let startFrame = Int(offsetSec * sampleRate) guard startFrame < original.frameLength else { return nil } let targetFrameCount = Int(original.frameLength) - startFrame guard let offsetBuf = AVAudioPCMBuffer(pcmFormat: original.format, frameCapacity: AVAudioFrameCount(targetFrameCount)) else { return nil } offsetBuf.frameLength = AVAudioFrameCount(targetFrameCount) // 按声道拷贝音频数据 let channelCount = Int(original.format.channelCount) for channelIdx in 0..<channelCount { guard let src = original.floatChannelData?[channelIdx], let dst = offsetBuf.floatChannelData?[channelIdx] else { continue } memcpy(dst, src.advanced(by: startFrame), targetFrameCount * MemoryLayout<Float>.size) } return offsetBuf }
注意事项
- 切换偏移位置前必须调用
player.stop()清空之前调度的旧buffer,否则旧内容会继续播放 - 偏移转帧计数时必须使用buffer本身格式的采样率,不要写死固定值
- 如果你的音频是整数PCM格式,拷贝数据时需要替换
floatChannelData为对应格式的int16ChannelData/int32ChannelData属性
内容的提问来源于stack exchange,提问作者Sirop4ik
相关产品推荐
相关产品推荐

