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

如何将AudioBufferList转换为AVAudioPCMBuffer?转换报错求助

问题解决:AudioBufferList转AVAudioPCMBuffer格式不匹配错误

错误原因

你遇到的报错the number of buffers (1) does not match the format's number of channel streams (2),核心是数据结构不匹配:

  • 你通过AudioUnitRender生成的AudioBufferList是交错(Interleaved)格式:1个AudioBuffer承载2个声道的混合数据
  • 但你创建AVAudioFormat时指定了interleaved: false,这要求格式为非交错(Non-Interleaved):每个声道对应1个独立的AudioBuffer,也就是需要2个buffer

解决方案一:修改AVAudioFormat为交错格式(推荐)

直接让音频格式和AudioBufferList的结构对齐,把interleaved参数改为true即可:

let audioFormat = AVAudioFormat(
    commonFormat: AVAudioCommonFormat.pcmFormatFloat64,
    sampleRate: recoder.sampleRate,
    interleaved: true, // 改为交错格式
    channelLayout: AVAudioChannelLayout(
        layoutTag: kAudioChannelLayoutTag_Stereo
    )!
)

guard let pcmBuffer = AVAudioPCMBuffer(
            pcmFormat: audioFormat,
            bufferListNoCopy: &bufferList
        ) else {
            return
        }

解决方案二:将AudioBufferList转换为非交错格式(如需非交错场景)

如果业务必须使用非交错格式,需要手动把交错数据拆分为独立的声道buffer:

// 假设AudioUnit输出的是Float64类型的交错音频数据
guard let interleavedData = bufferList.mBuffers.mData?.assumingMemoryBound(to: Float64.self) else {
    return
}
// 计算总帧数:字节数 / (单采样字节数 × 声道数)
let frameCount = Int(bufferList.mBuffers.mDataByteSize) / (MemoryLayout<Float64>.stride * 2)

// 创建非交错的AudioBufferList(2个buffer对应左右声道)
var nonInterleavedBufferList = AudioBufferList(
    mNumberBuffers: 2,
    mBuffers: (
        AudioBuffer(mNumberChannels: 1, mDataByteSize: UInt32(frameCount * MemoryLayout<Float64>.stride), mData: malloc(frameCount * MemoryLayout<Float64>.stride)),
        AudioBuffer(mNumberChannels: 1, mDataByteSize: UInt32(frameCount * MemoryLayout<Float64>.stride), mData: malloc(frameCount * MemoryLayout<Float64>.stride))
    )
)

// 拆分交错数据到两个声道
guard let leftData = nonInterleavedBufferList.mBuffers.0.mData?.assumingMemoryBound(to: Float64.self),
      let rightData = nonInterleavedBufferList.mBuffers.1.mData?.assumingMemoryBound(to: Float64.self) else {
    // 失败时释放内存避免泄漏
    free(nonInterleavedBufferList.mBuffers.0.mData)
    free(nonInterleavedBufferList.mBuffers.1.mData)
    return
}

for i in 0..<frameCount {
    leftData[i] = interleavedData[i * 2]     // 左声道数据:偶数索引
    rightData[i] = interleavedData[i * 2 + 1] // 右声道数据:奇数索引
}

// 创建非交错的AVAudioPCMBuffer
let audioFormat = AVAudioFormat(
    commonFormat: AVAudioCommonFormat.pcmFormatFloat64,
    sampleRate: recoder.sampleRate,
    interleaved: false,
    channelLayout: AVAudioChannelLayout(layoutTag: kAudioChannelLayoutTag_Stereo)!
)

guard let pcmBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat, bufferListNoCopy: &nonInterleavedBufferList) else {
    free(nonInterleavedBufferList.mBuffers.0.mData)
    free(nonInterleavedBufferList.mBuffers.1.mData)
    return
}
// 注意:使用bufferListNoCopy时,需保证数据内存的生命周期不短于AVAudioPCMBuffer,避免野指针

内容的提问来源于stack exchange,提问作者water or sapphire

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 20:50:29