制作程序化音频混音的有效方法

Posted

技术标签:

【中文标题】制作程序化音频混音的有效方法【英文标题】:Efficient way to make a Programmatic Audio Mixdown 【发布时间】:2009-06-09 21:12:07 【问题描述】:

我目前正在使用 Audio Units 开发 iPhone,并且我同时播放四首曲目。为了提高我的设置的性能,我认为最好通过将四个轨道混合为一个来最大限度地减少音频单元/线程的数量。

使用以下代码,我通过将四个轨道的样本相加来处理下一个缓冲区,将它们保持在 SInt16 范围内并将它们添加到临时缓冲区,稍后将复制到 ioData.mBuffers 的音频单元。

虽然它有效,但我不认为这是最有效的方法。

SInt16* buffer = bufferToWriteTo; 
int reads      = bufferSize/sizeof(SInt16);         
SInt16** files = circularBuffer->files;

float tempValue;
SInt16 values[reads];
int k,j;                
int numFiles=4;

for (k=0; k<reads; k++)

    tempValue=0.f; 
    for (j=0; j<numFiles; j++) 
    
        tempValue += files[j][packetNumber];        
    
    if      (tempValue >  32767.f) tempValue =  32767.f;
    else if (tempValue < -32768.f) tempValue =- 32768.f;

    values[k]  = (SInt16) tempValue;
    values[k] += values[k] << 16;
    packetNumber++;
    if (packetNumber >= totalPackets) packetNumber=0;

memcpy(buffer,values,bufferSize); 

有什么想法或建议可以加快速度吗?我说的对吗?

【问题讨论】:

【参考方案1】:

您可以从此代码中获得的最大改进是不使用浮点运算。虽然算法本身很快,但在嵌套循环中发生的转换需要很长时间,尤其是在 iPhone 的 ARM 处理器上。您可以通过对“tempValue”变量使用“SInt32”而不是“float”来获得完全相同的结果。

另外,看看你是否可以摆脱最后一个字符串中的 memcpy():也许你可以直接构造“缓冲区”,而不使用名为“值”的临时缓冲区。这样可以节省一份副本,对于这样的功能来说,这将是一个显着的改进。

其他注意事项:循环的最后两行可能属于循环之外,嵌套循环的主体应使用“k”作为第二个索引,而不是“packetNumber”,但我不确定这一点逻辑。

最后一个音符:你正在挤压你产生的声音的峰值。虽然这看起来是个好主意,但听起来很粗糙。您可能希望缩小结果而不是裁剪它。像这样:而不是这段代码

for (j=0; j<numFiles; j++) 

    tempValue += files[j][packetNumber];            

if      (tempValue >  32767.f) tempValue =  32767.f;
else if (tempValue < -32768.f) tempValue =- 32768.f;

你可能想要这样的东西:

for (j=0; j<numFiles; j++) 

    tempValue += files[j][packetNumber] / numFiles;            

编辑:请不要忘记测量前后的性能,看看哪一项改进产生了最大的影响。这是学习性能的最佳方式:试验和测量

【讨论】:

【参考方案2】:

尽管我对 iPhone 开发不是很熟悉,但请给我一些建议。

您可以展开内部循环。您不需要 for 循环来将 4 个数字相加,尽管您的编译器可能会为您执行此操作。

在 for 循环中直接写入缓冲区。最后的 memcpy 将执行另一个循环来复制缓冲区。

不要对 tempvalue 使用浮点数。取决于硬件整数数学更快,你不需要对通道求和的浮点数。

删除 if/endif。无论如何,数字削波听起来很可怕,所以在将通道加在一起之前尽量避免它。如果可能的话,应该避免在这样的循环内分支。

【讨论】:

感谢您的提示!直接写入缓冲区是一个不错的选择。尽管如此,我相信 iPhone 上的浮点数学运算速度更快。 数学运算可能很快,但从 int 到 float 再返回的转换可能很慢。但是罗姆已经告诉过你了。试一试并衡量 :-) 分析总是比谈论性能更好。 我明白了。会尝试的。谢谢。【参考方案3】:

在为我的应用程序编写音频混合例程时,我发现一件事是递增指针的工作速度比索引快得多。一些编译器可能会为您解决这个问题,但 - 在 iphone 上不确定 - 但肯定这为我的应用程序提供了这些紧密循环的巨大提升(如果我记得的话,大约是 30%)。

例如:而不是这个:

for (k=0; k<reads; k++)

    // Use buffer[k]

这样做:

SInt16* p=buffer;
SInt16* pEnd=buffer+reads;
while (p!=pEnd)

    // Use *p
    p++;

另外,我相信 iPhone 具有某种称为 VFP 的 SIMD(单指令多数据)支持。这可以让您在一条指令中对多个样本进行数学运算,但我在 iPhone 上对此知之甚少。

【讨论】:

以上是关于制作程序化音频混音的有效方法的主要内容,如果未能解决你的问题,请参考以下文章

音频混音是啥

将 AudioEffect 附加到全局混音的首选方式?

最实用的虚拟声卡-Windows安装虚拟声卡(有效解决PR音频输入,AU录音,obs多音频原控制等问题)

最实用的虚拟声卡-Windows安装虚拟声卡(有效解决PR音频输入,AU录音,obs多音频原控制等问题)

最实用的虚拟声卡-Windows安装虚拟声卡(有效解决PR音频输入,AU录音,obs多音频原控制等问题)

最实用的虚拟声卡-Windows安装虚拟声卡(有效解决PR音频输入,AU录音,obs多音频原控制等问题)