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

如何在AURenderCallback中将两个Audio Unit合并到AudioBufferList

解决方案:合并buffList1与buffList2到ioData

首先要明确你需要的是音频混音(叠加播放)还是音频拼接(先后播放),两种场景的实现逻辑不同,以下是具体实现:

前提确认

  • 确保buffList1、buffList2与ioData的音频格式完全一致(采样率、位深、通道数、字节序)
  • 保证ioData的缓冲区有足够空间容纳合并后的数据,避免内存越界

场景1:音频混音(两个音频叠加)

如果需要同时播放两个音频,将波形叠加,需逐样本相加并处理溢出:

32位浮点PCM(iOS/macOS常见格式)

OSStatus PlayCallback(void *inRefCon,
                        AudioUnitRenderActionFlags *ioActionFlags,
                        const AudioTimeStamp *inTimeStamp,
                        UInt32 inBusNumber,
                        UInt32 inNumberFrames,
                        AudioBufferList *ioData) {
    ALNPlayer *player = (__bridge ALNPlayer *)inRefCon;
    
    OSStatus status;
    
    // 读取音频1数据到buffList1,检查错误
    status = AudioConverterFillComplexBuffer(player->audioConverter1, lyInInputDataProcV1, inRefCon, &inNumberFrames, player->buffList1, NULL);
    if (status != noErr) return status;
    
    // 读取音频2数据到buffList2,检查错误
    status = AudioConverterFillComplexBuffer(player->audioConverter2, lyInInputDataProcV2, inRefCon, &inNumberFrames, player->buffList2, NULL);
    if (status != noErr) return status;
    
    // 转换为浮点指针(单通道场景)
    float *ioPtr = (float *)ioData->mBuffers[0].mData;
    float *buff1Ptr = (float *)player->buffList1->mBuffers[0].mData;
    float *buff2Ptr = (float *)player->buffList2->mBuffers[0].mData;
    
    // 逐样本叠加,限幅防止失真(浮点范围[-1.0, 1.0])
    for (UInt32 i = 0; i < inNumberFrames; i++) {
        float mixed = buff1Ptr[i] + buff2Ptr[i];
        ioPtr[i] = fmaxf(fminf(mixed, 1.0f), -1.0f);
    }
    
    // 更新ioData的字节数
    ioData->mBuffers[0].mDataByteSize = inNumberFrames * sizeof(float);
    
    return noErr;
}

16位整型PCM

如果是16位整数格式,需用32位整型中转避免溢出:

// 转换为16位整型指针
SInt16 *ioPtr = (SInt16 *)ioData->mBuffers[0].mData;
SInt16 *buff1Ptr = (SInt16 *)player->buffList1->mBuffers[0].mData;
SInt16 *buff2Ptr = (SInt16 *)player->buffList2->mBuffers[0].mData;

for (UInt32 i = 0; i < inNumberFrames; i++) {
    SInt32 sum = (SInt32)buff1Ptr[i] + (SInt32)buff2Ptr[i];
    // 限幅到16位范围[-32768, 32767]
    sum = fmaxf(fminf(sum, 32767), -32768);
    ioPtr[i] = (SInt16)sum;
}

ioData->mBuffers[0].mDataByteSize = inNumberFrames * sizeof(SInt16);

场景2:音频拼接(先播放buffList1,再播放buffList2)

如果需要按顺序播放两个音频,直接拼接缓冲区即可:

OSStatus PlayCallback(void *inRefCon,
                        AudioUnitRenderActionFlags *ioActionFlags,
                        const AudioTimeStamp *inTimeStamp,
                        UInt32 inBusNumber,
                        UInt32 inNumberFrames,
                        AudioBufferList *ioData) {
    ALNPlayer *player = (__bridge ALNPlayer *)inRefCon;
    
    OSStatus status;
    
    // 读取音频1数据到buffList1
    status = AudioConverterFillComplexBuffer(player->audioConverter1, lyInInputDataProcV1, inRefCon, &inNumberFrames, player->buffList1, NULL);
    if (status != noErr) return status;
    
    // 读取音频2数据到buffList2
    status = AudioConverterFillComplexBuffer(player->audioConverter2, lyInInputDataProcV2, inRefCon, &inNumberFrames, player->buffList2, NULL);
    if (status != noErr) return status;
    
    UInt32 buff1Size = player->buffList1->mBuffers[0].mDataByteSize;
    UInt32 buff2Size = player->buffList2->mBuffers[0].mDataByteSize;
    
    // 先复制buffList1到ioData起始位置
    memcpy(ioData->mBuffers[0].mData, player->buffList1->mBuffers[0].mData, buff1Size);
    // 再复制buffList2到buffList1的末尾
    void *offsetPtr = (uint8_t *)ioData->mBuffers[0].mData + buff1Size;
    memcpy(offsetPtr, player->buffList2->mBuffers[0].mData, buff2Size);
    
    // 更新ioData的总字节数
    ioData->mBuffers[0].mDataByteSize = buff1Size + buff2Size;
    
    return noErr;
}

额外注意事项

  • 多通道场景:如果是立体声或多通道,需要遍历AudioBufferList中的每个mBuffers元素,分别处理每个通道的数据
  • 缓冲区大小:拼接场景下,要确保ioData的缓冲区大小≥buff1Size + buff2Size,否则会触发内存越界
  • 错误处理:每次调用AudioConverterFillComplexBuffer后必须检查返回状态,避免处理无效数据

内容的提问来源于stack exchange,提问作者Alan Luo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 21:15:35