混合16位线性PCM流并避免削波/溢出

时间:2012-08-23 10:31:58

标签: c audio signal-processing pcm mixing

我试图将2个16位线性PCM音频流混合在一起,但我似乎无法克服噪音问题。我认为将样品混合在一起时,它们会溢出。

我有以下功能...

short int mix_sample(short int sample1, short int sample2)
{
    return #mixing_algorithm#;
}

......以及我作为#mixing_algorithm#

尝试的内容
sample1/2 + sample2/2
2*(sample1 + sample2) - 2*(sample1*sample2) - 65535
(sample1 + sample2) - sample1*sample2
(sample1 + sample2) - sample1*sample2 - 65535
(sample1 + sample2) - ((sample1*sample2) >> 0x10) // same as divide by 65535

其中一些产生了比其他产品更好的效果,但即使最好的结果也包含了很多噪音。

任何想法如何解决?

6 个答案:

答案 0 :(得分:10)

我找到的最佳解决方案是given by Viktor Toth。他为8位无符号PCM提供了解决方案,并为16位带符号PCM改变了这一点,产生了这样的结果:

int a = 111; // first sample (-32768..32767)
int b = 222; // second sample
int m; // mixed result will go here

// Make both samples unsigned (0..65535)
a += 32768;
b += 32768;

// Pick the equation
if ((a < 32768) || (b < 32768)) {
    // Viktor's first equation when both sources are "quiet"
    // (i.e. less than middle of the dynamic range)
    m = a * b / 32768;
} else {
    // Viktor's second equation when one or both sources are loud
    m = 2 * (a + b) - (a * b) / 32768 - 65536;
}

// Output is unsigned (0..65536) so convert back to signed (-32768..32767)
if (m == 65536) m = 65535;
m -= 32768;

使用此算法意味着几乎不需要剪切输出,因为它只是一个不在范围内的值。与直线平均不同,即使另一个源静音,也不会减少一个源的音量。

答案 1 :(得分:8)

这是一个描述性的实现:

short int mix_sample(short int sample1, short int sample2) {
    const int32_t result(static_cast<int32_t>(sample1) + static_cast<int32_t>(sample2));
    typedef std::numeric_limits<short int> Range;
    if (Range::max() < result)
        return Range::max();
    else if (Range::min() > result)
        return Range::min();
    else
        return result;
}

混合,它只是添加和剪辑!

为了避免剪切伪像,您需要使用饱和度或限制器。理想情况下,您将拥有一个带有少量前瞻的小int32_t缓冲区。这将引入延迟。

比限制所有地方更常见的是在信号中留下一些“净空”值。

答案 2 :(得分:2)

这是我在最近的合成器项目中所做的。

int* unfiltered = (int *)malloc(lengthOfLongPcmInShorts*4);
int i;
for(i = 0; i < lengthOfShortPcmInShorts; i++){
    unfiltered[i] = shortPcm[i] + longPcm[i];
}
for(; i < lengthOfLongPcmInShorts; i++){
     unfiltered[i] = longPcm[i];
}

int max = 0;
for(int i = 0; i < lengthOfLongPcmInShorts; i++){
   int val = unfiltered[i];
   if(abs(val) > max)
      max = val;
}

short int *newPcm = (short int *)malloc(lengthOfLongPcmInShorts*2);
for(int i = 0; i < lengthOfLongPcmInShorts; i++){
   newPcm[i] = (unfilted[i]/max) * MAX_SHRT;
}

我将所有PCM数据添加到整数数组中,这样我就可以得到所有未经过滤的数据。

在这之后我在整数数组中查找绝对最大值。

最后,我取整数数组并将其放入一个短的int数组中,将每个元素除以该最大值,然后乘以max short int值。

通过这种方式,您可以获得满足数据所需的最小“净空”量。

您可以对整数数组进行一些统计并整合一些剪辑,但是对于我需要的最小量的余量对我来说已经足够了。

答案 3 :(得分:1)

这里有一个讨论:https://dsp.stackexchange.com/questions/3581/algorithms-to-mix-audio-signals-without-clipping,为什么A + B-A * B解决方案不理想。在此讨论的评论中,隐藏了一项建议,即对这些值求和并除以信号数量的平方根。额外的裁剪检查不会受到伤害。这似乎是一个合理的(简单而快速的)中间立场。

答案 4 :(得分:0)

我认为它们应该是映射[MIN_SHORT, MAX_SHORT] -> [MIN_SHORT, MAX_SHORT]的函数,它们显然不是(除了第一个),所以会发生溢出。

如果unwind的命题不起作用,你也可以尝试:

((long int)(sample1) + sample2) / 2

答案 5 :(得分:-1)

由于您在时域中,频率信息在连续样本之间的差异中,当您除以2时会损坏该信息。这就是为什么添加和剪辑效果更好的原因。剪切当然会增加非常高的频率噪声,可能会过滤掉。