为什么当我降低串联全通滤波器的增益时音量会增加?

信息处理 声音的 全部通过 混响
2022-02-20 09:29:25

我正在使用 Schroeder Manfred R 提出的无色全通滤波器来创建混响器。它由大约 8 个串联的无色全通滤波器组成,每个滤波器具有不同的延迟长度。

收益g使用以下等式将所有无色全通滤波器设置为所需的混响时间:

g=103dapf/t

dapf是每个全通滤波器的延迟时间,以秒为单位,并且t是整个混响器的混响时间,以秒为单位。

问题是当我将混响时间从高值设置为低值时,混响器的整体音量迅速增加。

但是,成交量不会发散;当我完成混响时间设置后,它会恢复正常。

我将混响时间设置得越快,音量的增加就越大,而如果我将混响时间设置得慢,则音量不会增加太多。

我无法确定,因为我无法准确听到发生了什么,但另一方面,当我将混响时间从低值设置为高值时,音量可能会降低。

你认为什么可能导致这个问题?另外,我想听听您对如何解决这个问题的建议。

Schroeder,Manfred R 的无色全通滤波器

无色全通滤波器类 (C++):

class ColorlessAPF
{
public:
  // constructor
  ColorlessAPF(float delay, float gain);

  // common method
  void apply(float* in_L, float* in_R = nullptr);

  // getter
  float getDelay() { return delay; }
  float getGain() { return gain; }

  // setter
  void setGain(float value) { gain = value; }
  void setSamplerate(double value);

private:
  // member
  double samplerate = 44'100.0;
  const float delay;
  float gain;

  using Buffer = std::unique_ptr<RingBuffer<float>>;
  Buffer buffer[2];
};

inline ColorlessAPF::ColorlessAPF(float delay, float gain) :
  delay(delay),
  gain(gain)
{
  setSamplerate(samplerate);
}

inline void ColorlessAPF::apply(float* in_L, float* in_R)
{
  const int num_loops = in_R ? 2 : 1;
  for (int i = 0; i < num_loops; i++) {
    float* in = i == 0 ? in_L : in_R;
    float from_buffer = buffer[i].get()->getFront();
    float to_buffer = *in + from_buffer * gain;
    buffer[i].get()->push(to_buffer);
    float output = *in * -gain + from_buffer * (1.0f - gain * gain);
    *in = output;
  }
}

inline void ColorlessAPF::setSamplerate(double value)
{
  samplerate = value;
  int buffer_size = value * delay / 1'000.0f;
  buffer[0].reset(new RingBuffer<float>(buffer_size));
  buffer[1].reset(new RingBuffer<float>(buffer_size));
}

/* and And how I set the reverberation time:
void setReverbTime(float value)
{
  for (auto&& item : apfs) {
    item.get()->setGain(-std::pow(10, (-3 * item.get()->getDelay() / 1'000) / value));
  }
}
*/
2个回答

这是 Schroeder allpass 的一个非常奇怪的实现。施罗德全通可以很容易地通过扭曲一个正常的全通而得到,它的传递函数很简单

H(z)=g+zM1+gzM

对应的差分方程为

y[n]=gx[n]+x[nM]gy[nM]

这个差分方程可以在四种“正常”拓扑中的任何一种中实现,即直接形式 I、转置形式 I、直接形式 II、转置形式 II。您的实现不是其中之一。

如果要动态更新过滤器,则需要观察从输入到状态变量的传递函数。虽然从输入到输出的传递函数是平坦的,但从输入到状态变量的传递函数通常不是。这会在您更新过滤器时创建音量更改。

这里最简单的解决方案可能是以直接形式 I 实现这一切。这样,唯一的状态变量是输入和输出,并且保证传递函数没有增益。不利的一面是,这需要两倍的状态内存量,因为您需要延迟输入和输出。但是,由于您使用的是 8 级联部分,因此您可以将阶段 K 的输出延迟用作阶段 K+1 的输入延迟,因此您的内存消耗只会增加 9/8。

如果没有看到全通滤波器的细节,我不能肯定地说,但是当你改变它的参数时,IIR 滤波器表现出输出的瞬态增加或减少是常见的和不可避免的。

问题是状态通常对参数有意义——所以当你改变参数而不调整状态来补偿时,你基本上是在重新初始化过滤器,而不是随机的,因为没有意义的状态。

两种可能的解决方法是限制延迟的变化速度,以免注意到音量变化,或者深入研究 IIR 滤波器设计并重新设计全通滤波器,这样它们就不会以相同的方式响应瞬变。

我不知道您使用的是什么处理器,但是在这些内存便宜的日子里,为什么不使用足够大的环形缓冲区来容纳您的最长延迟,并根据您选择信号的位置来设置实际延迟时间呢?