AVAudioEngine多通道转单通道及格式转换异常问题求助
问题分析与代码修复建议
我帮你排查出代码里几个关键问题,正好对应你遇到的三个异常情况,下面逐个拆解并给出修复方案:
1. 96k采样率下仅提取单侧通道信号
这个问题出在MicSinkNode对多通道音频的错误处理,以及WAV文件写入的字节序错误:
- 当音频接口是立体声格式时,
audioBufferList会包含左右两个声道的Buffer,但你的代码只读取了第一个Buffer的数据,自然只能拿到单侧信号;如果是交错格式的立体声,还会出现“间隔采样”的诡异效果。 - WAV文件写入时设置了
AVLinearPCMIsBigEndianKey: true,但标准WAV采用小端字节序,这会导致DAW解析时出现通道错误,加重异常表现。
修复代码:
首先修正WAV写入的字节序:
let outputFormatSettings = [ AVFormatIDKey: kAudioFormatLinearPCM, AVLinearPCMBitDepthKey: 32, AVLinearPCMIsFloatKey: true, AVLinearPCMIsBigEndianKey: false, // 改为小端字节序 AVSampleRateKey: SAMPLE_RATE, AVNumberOfChannelsKey: 1 ] as [String : Any]
然后修正MicSinkNode的多通道处理(兼容formatMixer转换失效的情况,手动混合多通道到单声道):
let MicSinkNode = AVAudioSinkNode() { (timeStamp, frames, audioBufferList) -> OSStatus in if self.in1firstRun == true { let start1 = CACurrentMediaTime() self.in1StartTime = Double(start1) self.in1firstRun = false } // 提前判断是否停止录制 if !self.running { return noErr } let abl = UnsafeMutableAudioBufferListPointer(audioBufferList) let frameCount = Int(frames) var monoSamples = [Float](repeating: 0, count: frameCount) // 手动混合所有通道到单声道 for buffer in abl { let bufPtr = UnsafeMutableBufferPointer<Float>(buffer) for i in 0..<frameCount { monoSamples[i] += bufPtr[i] / Float(abl.count) } } // 检查缓冲区是否溢出 if self.buf1current + frameCount >= self.in1Buf.count { self.running = false return noErr } // 用memcpy替代循环拷贝,提升效率 memcpy(&self.in1Buf[self.buf1current], monoSamples, frameCount * MemoryLayout<Float>.stride) self.buf1current += frameCount return noErr }
2. 同采样率下录制时长过长
原因是刺激声播放结束后,引擎仍在录制静音数据:
- 你的sourceNode在播放完刺激声后会持续输出0,但MicSinkNode的停止条件是
buf1current >=100000,这会额外录制大量静音。 running变量的同步逻辑不一致,sourceNode设置running=false后,MicSinkNode没有及时响应停止。
修复代码:
在sourceNode中,播放完刺激声后主动停止引擎(注意在非音频线程操作引擎):
if Int(frameCount) + time >= inputSweep.stimulus.count{ self.running = false print("AUDIO ENGINE STOPPED") DispatchQueue.main.async { self.engine.stop() } }
3. 44.1k转48k时出现卡顿
这个问题源于采样率转换的配置缺失和音频线程的低效操作:
- 你请求了应用采样率,但没有检查系统是否接受,当接口固定为44.1k时,引擎会强制进行采样率转换,若缓冲区配置不合理就会卡顿。
- MicSinkNode中的数组append和循环拷贝效率低下,拖慢音频线程。
修复代码:
首先完善AVAudioSession的配置,添加缓冲区时长设置,并检查实际采样率:
do { try AVAudioSession.sharedInstance().setCategory(.playAndRecord) // 设置10ms缓冲区,平衡延迟和稳定性 try AVAudioSession.sharedInstance().setPreferredIOBufferDuration(0.01) // 请求采样率并获取实际生效的值 try AVAudioSession.sharedInstance().setPreferredSampleRate(Double(sampleRate)) print("实际生效采样率:\(AVAudioSession.sharedInstance().sampleRate)") } catch { assertionFailure("AVAudioSession setup failed: \(error.localizedDescription)") }
其他冗余/风险点修复
- 移除引擎初始化时的冗余代码:不需要重新实例化
engine,reset()已足够重置状态:
// 替换原来的stop/reset/reinit逻辑 engine.stop() engine.reset() // 删掉 engine = AVAudioEngine()
- 不要用
try! engine.start(),改用do-catch捕获错误避免崩溃:
do { try engine.start() } catch { assertionFailure("引擎启动失败:\(error.localizedDescription)") running = false }
内容的提问来源于stack exchange,提问作者samp17
相关产品推荐
相关产品推荐

