Swift开发中如何调整已录制音频的音高与播放速度且不破坏原有功能
不需要完全重写AudioPlayer.swift,也不需要维护两套播放逻辑,直接统一用AVAudioEngine替换原有AVAudioPlayer的内部实现即可,对外接口保持和原有实现一致,上层业务代码无需任何改动。
具体改造方案
- 替换内部核心属性
删掉原有AVAudioPlayer相关属性,新增AVAudioEngine链路核心节点:
class AudioPlayer { private let engine = AVAudioEngine() private let playerNode = AVAudioPlayerNode() // 该节点同时支持调速、调pitch,无需额外新增节点 private let pitchControl = AVAudioUnitTimePitch() private var currentAudioFile: AVAudioFile? }
- 初始化时完成链路配置
init() { // 挂载节点到引擎 engine.attach(playerNode) engine.attach(pitchControl) // 配置节点链路:播放节点 -> 音效处理节点 -> 输出混音器 engine.connect(playerNode, to: pitchControl, format: nil) engine.connect(pitchControl, to: engine.mainMixerNode, format: nil) // 提前启动引擎,避免首次播放延迟 try? engine.start() }
- 兼容原有对外接口
保持原有方法名、参数、返回值完全不变,仅替换内部实现逻辑,上层调用无需修改:
// 原有播放方法,对外接口完全对齐原实现 func startPlayback(audio: URL) throws { // 先重置之前的播放状态 stopPlayback() // 加载音频文件 currentAudioFile = try AVAudioFile(forReading: audio) guard let file = currentAudioFile else { return } // 调度音频播放 playerNode.scheduleFile(file, at: nil) playerNode.play() } // 原有停止方法,兼容旧逻辑 func stopPlayback() { playerNode.stop() playerNode.reset() } // 兼容原有播放进度获取逻辑 var currentTime: TimeInterval { guard let lastRenderTime = playerNode.lastRenderTime, let playerTime = playerNode.playerTime(forNodeTime: lastRenderTime), let file = currentAudioFile else { return 0 } return Double(playerTime.sampleTime) / file.processingFormat.sampleRate } // 兼容原有总时长获取逻辑 var duration: TimeInterval { currentAudioFile?.duration ?? 0 }
- 新增音高、速度调整接口
直接对外暴露音效节点的参数即可:
// 播放速度,建议取值范围0.5~2.0,默认1.0为原始速度 var playbackRate: Float { get { pitchControl.rate } set { pitchControl.rate = newValue } } // 音高,单位为音分,100音分对应1个半音,建议取值范围-2400~2400,默认0为原始音高 var playbackPitch: Float { get { pitchControl.pitch } set { pitchControl.pitch = newValue } }
注意事项
AVAudioUnitTimePitch默认rate=1、pitch=0时,播放效果和原生AVAudioPlayer完全一致,无需区分是否开启音效,所有播放场景都可以用这套实现,性能远高于切换两套播放逻辑的方案。
如果需要支持暂停功能,新增pause()方法调用playerNode.pause()即可,原有逻辑无需调整。
内容的提问来源于stack exchange,提问作者phiredrop
相关产品推荐
相关产品推荐

