为基本混响找到好的反馈系数

信息处理 声音的 算法 即时的
2022-02-10 00:15:59

我目前正在为 Gamebody Advance 编写一些混响算法代码,以便为音乐获得一些环境感觉,而不是让声音变得平淡无奇。一些具有挑战性的限制是 GBA 的低 CPU 速度(~16 MHz ARM CPU)和我在 31536 Hz 时可用于混响缓冲区的 4KiB 内存。也许我可以以一半的速度进行混响以节省一些记忆。目标是将 CPU 预算保持在 5% 以下,我很清楚这是一个非常迫切的目标。我尝试了一些简单的东西,比如简单的反馈线,但结果并没有那么好,听起来更像是回声而不是实际的混响。但是,我可以让 ASM 以大约 2-3% 的 CPU 时间工作。

因此,在尝试在硬件上实现任何东西之前,我编写了一个小 C++ 应用程序来尝试一些算法,并且没有在 GBA 上没有浮点数的怪癖。这是我想出的核心代码:

// processing is done inplace on "buffer"
// samples ares stored L/R alternating
//
// reverbBuffer is a simple mono buffer
// buffer wraparound is already taken care of
//
// My reverb buffer positions are selected by being 'approximately' coprime
//
// NBPOS = 3 (number of taps on the buffer)

for (size_t i = 0; i < todo; i++) {
    float dryL = buffer[0];
    float dryR = buffer[1];
    float wetL = 0.f;
    float wetR = 0.f;
    for (int i = 0; i < NBPOS; i++) {
        wetL += coeffL[i] * reverbBuffer[bufferPos[i]];
        wetR += coeffR[i] * reverbBuffer[bufferPos[i]];
    }

    *buffer++ = dryL + wetMix * wetL;
    *buffer++ = dryR + wetMix * wetR;

    reverbBuffer[bufferPos[0]] = ((dryL + dryR) * 0.5f) * dryMix + ((wetL + wetR) * 0.5f);
    for (int i = 0; i < NBPOS; i++)
        bufferPos[i]++;
}

为了测试,我在几个 44.1kHz 的波形文件上使用它,并带有适当更长的混响缓冲区。

所以,我们的结果……好坏参半。我添加了一些逻辑来为反馈系数(-1.0 到 1.0)、“dryMix”和“wetMix”变量(0.0 到 1.0)创建随机值。取决于我有多幸运,要么得到了可怕的金属振荡,要么得到了相当好的结果(我仍然认为没有什么是完美的)。

有什么方法可以为我的反馈抽头找到好的系数吗?除了声音不好之外,我还遇到了不稳定和自振的问题。我没有任何专业的 DSP 知识,但我猜我所有系数的绝对值之和不应超过 1.0(但是对于负系数而言,这似乎并不总是正确的)。

此外,如果没有办法以很有意义的方式计算这些系数,是否至少有一种方法可以随机生成这些系数,而不会导致自振荡?

PS:我在谷歌上搜索了很多关于混响和各种东西的信息,但我没有找到完全适合我的算法。通常它们太基本(单击反馈线)或者它们不够快(因为我需要一些非常快且内存占用少的东西)。

1个回答

我不知道如何轻松计算混响拓扑的稳定性标准,但是通过计算脉冲响应很容易检查一组延迟和权重是否会产生稳定的滤波器(除非它是边缘稳定或边缘不稳定)看看它是否会爆炸。

调整混响的最简单方法是使用全局优化,如差分进化,您实际上尝试不同的延迟和权重,并针对某些成本函数优化它们。成本函数可以是这样的,它计算混响的脉冲响应并惩罚不稳定性,计算脉冲响应的离散傅里叶变换 (DFT) 以获得频率响应,使用快速傅里叶变换 (FFT),并奖励某些东西像光谱平坦度

如果您不熟悉这些方法,则工作量适中。