我有两个原始声音流,我需要加在一起.出于这个问题的目的,我们可以假设它们具有相同的比特率和比特深度(例如16比特样本,44.1khz采样率).
显然,如果我只是将它们加在一起,我将溢出并下溢我的16位空间.如果我将它们加在一起并除以2,那么每个的体积减半,这是不正确的声音 - 如果两个人在一个房间里说话,他们的声音不会变得安静一半,麦克风可以选择它们两者都没有击中限制器.
那么在我的软件混音器中将这些声音组合在一起的正确方法是什么?
我错了,正确的方法是将每个的体积减少一半?
我是否需要添加压缩器/限制器或其他处理阶段以获得我正在尝试的音量和混音效果?
-亚当
您应该将它们一起添加,但将结果剪切到允许的范围以防止上溢/下溢.
如果发生剪辑,您将在音频中引入失真,但这是不可避免的.您可以使用裁剪代码"检测"这种情况并将其报告给用户/操作员(相当于混音器上的红色"剪辑"灯......)
你可以实现一个更"适当"的压缩器/限制器,但不知道你的确切应用,很难说它是否值得.
如果您正在进行大量的音频处理,您可能希望将音频级别表示为浮点值,并且只在该过程结束时返回到16位空间.高端数字音频系统通常以这种方式工作.
我更愿意评论两个排名很高的回复之一,但由于我的声誉微薄(我猜)我不能.
"勾选"答案:加在一起并且剪辑是正确的,但如果你想避免剪裁则不是.
链接的答案从[0,1]中两个正信号的可行巫术算法开始,然后应用一些非常错误的代数来导出一个完全不正确的有符号值和8位值的算法.该算法也不会扩展到三个或更多输入(信号的乘积将在总和增加时下降).
因此 - 将输入信号转换为浮点数,将它们缩放到[0,1](例如,有符号的16位值将变为
float v = ( s + 32767.0 ) / 65536.0 (close enough...))
然后求和.
要缩放输入信号,您应该做一些实际工作,而不是乘以或减去巫毒值.我建议保持一个运行平均音量,然后如果它开始漂移高(高于0.25说)或低(低于0.01说)开始应用基于音量的缩放值.这基本上成为一个自动级别的实现,它可以随任意数量的输入进行扩展.最重要的是,在大多数情况下,它根本不会弄乱您的信号.
有一个关于混合的文章在这里.我有兴趣知道其他人对此的看法.
大多数音频混合应用程序将使用浮点数进行混合(32位足以混合少量流).将16位样本转换为浮点数,范围-1.0到1.0表示16位世界中的满量程.然后将样本汇总在一起 - 你现在有足够的空间.最后,如果你得到任何值超过满量程的样本,你可以衰减整个信号或使用硬限制(削波值为1.0).
与将16位样本相加并让它们溢出相比,这将产生更好的声音效果.这是一个非常简单的代码示例,展示了如何将两个16位样本相加:
short sample1 = ...; short sample2 = ...; float samplef1 = sample1 / 32768.0f; float samplef2 = sample2 / 32768.0f; float mixed = samplef1 + sample2f; // reduce the volume a bit: mixed *= 0.8; // hard clipping if (mixed > 1.0f) mixed = 1.0f; if (mixed < -1.0f) mixed = -1.0f; short outputSample = (short)(mixed * 32768.0f)
"安静一半"并不完全正确.由于耳朵的对数响应,将样本分成两半会使6-db更安静 - 当然明显,但不是灾难性的.
您可能希望通过乘以0.75来妥协.这将使3-db更安静,但是会减少溢出的可能性,并且当它确实发生时也会减少失真.
我无法相信没有人知道正确的答案.每个人都足够接近,但仍然是一种纯粹的哲学.最近的,即最好的是:(s1 + s2) - (s1*s2).这是一种非常好的方法,特别是对于MCU.
所以,算法是:
找出您想要输出声音的音量.它可以是其中一个信号的平均值或最大值.
factor = average(s1)
您假设两个信号都已正常,而不是溢出32767.0
使用此因子标准化两个信号:
s1 = (s1/max(s1))*factor
s2 = (s2/max(s2))*factor
将它们添加到一起并使用相同的因子对结果进行标准化
output = ((s1+s2)/max(s1+s2))*factor
请注意,在步骤1之后,您实际上不需要返回到整数,您可以使用-1.0到1.0间隔的浮点数,并使用先前选择的功率因数将返回值应用于最后的整数.我希望我现在没有弄错,因为我很着急.
您还可以为自己购买一些头部空间,如曲线y = 1.1x - 0.2x ^ 3,顶部和底部都有一个帽子.当玩家一起演奏多个音符时(最多6个音符),我在Hexaphone中使用了它.
float waveshape_distort( float in ) { if(in <= -1.25f) { return -0.984375; } else if(in >= 1.25f) { return 0.984375; } else { return 1.1f * in - 0.2f * in * in * in; } }
它不是防弹 - 但会让你达到1.25级别,并使剪辑平滑到一个漂亮的曲线.产生谐波失真,听起来比削波更好,在某些情况下可能是理想的.