NAudio实时均衡器

信息处理 matlab 离散信号 均衡器
2022-02-24 17:03:35

我在 Matlab 中使用 fdatool 设计了 ​​9 个 IIR 带通滤波器(chebyshev type 1,order 4)。然后我使用 a 和 b 滤波器系数将其应用于微分方程。所以,我的问题是如何使用滑块应用每个带通滤波器的(实时)增益?(如何为某个过滤器添加增益或损失?)

这是我在 C# 中的代码:

public void initializeFilters(){
    filters.Clear();
    //9 Band pass filters
    for (int i = 0; i < 9; i++)
        filters.Add(new Filter());

    //F1=40hz Fc=50Hz F2=60Hz
    float k = (float)Math.Pow(1.0, -5);
    filters[0].B = new float[] { k * 0.1992f, 0, k * -0.3983f, 0, k * 0.1992f }; 
    filters[0].A = new float[] { 1.0000f, -3.9968f, 5.9904f, -3.9905f, 0.9969f };

    //60Hz 80Hz 100Hz
    k = (float)Math.Pow(1.0, -4);
    filters[1].B = new float[] { k * 0.0795f, 0, k * -0.1591f, 0, k * 0.0795f };
    filters[1].A = new float[] { 1.0000f, -3.9935f, 5.9807f, -3.9810f, 0.9938f };

    //100Hz 130Hz 160Hz
    filters[2].B = new float[] { k * 0.1787f, 0, k * -0.3574f, 0, k * 0.1787f };
    filters[2].A = new float[] { 1.0000f, -3.9899f, 5.9705f, -3.9713f, 0.9907f };

    //160Hz 350Hz 600Hz
    filters[3].B = new float[] { 0.0009f, 0, -0.0019f, 0, 0.0009f };
    filters[3].A = new float[] { 1.0000f, -3.9255f, 5.7847f, -3.7927f, 0.9335f };

    //600Hz 1300Hz 2000Hz
    filters[4].B = new float[] { 0.0088f, 0, -0.0176f, 0, 0.0088f };
    filters[4].A = new float[] { 1.0000f, -3.7187f, 5.2467f, -3.3315f, 0.8040f };

    //2000Hz 4000Hz 6000Hz
    filters[5].B = new float[] { 0.0595f, 0, -0.1191f, 0, 0.0595f };
    filters[5].A = new float[] { 1.0000f, -2.8765f, 3.4528f, -2.0797f, 0.5459f };

    //6000Hz 8000Hz 10000Hz
    filters[6].B = new float[] { 0.0595f, 0, -0.1191f, 0, 0.0595f };
    filters[6].A = new float[] { 1.0000f, -1.4273f, 1.8140f, -1.0319f, 0.5459f };

    //10000Hz 12000Hz 14000Hz
    filters[7].B = new float[] { 0.0595f, 0, -0.1191f, 0, 0.0595f };
    filters[7].A = new float[] { 1.0000f, 0.4731f, 1.3375f, 0.3420f, 0.5459f };

    //14000Hz 16000Hz 18000Hz
    filters[8].B = new float[] { 0.0595f, 0, -0.1191f, 0, 0.0595f };
    filters[8].A = new float[] { 1.0000f, 2.2239f, 2.5782f, 1.6078f, 0.5459f };
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
    int read = sourceStream.Read(buffer, offset, count);
    float[] f_read = new float[read / 4], y = new float[read / 4];
    Buffer.BlockCopy(buffer, offset, f_read, 0, count);

    for (int n = 0; n < read / 4; n++)
        y[n] = 0;

    for (int i = 0; i < filters.Count; i++)
    {
        for (int n = 0; n < read / 4; n++)
        {
            for (int k = 0; k < filters[i].B.Length; k++)
            {
                if (n - k >= 0)
                    y[n] = y[n] + filters[i].B[k] * f_read[n - k];
            }
            for (int k = 1; k < filters[i].A.Length; k++)
            {
                if (n - k >= 0)
                    y[n] = y[n] - filters[i].A[k] * y[n - k];
            }
        }
    }

    for (int n = 0; n < read / 4; n++)
        y[n]= Math.Min(1, Math.Max(-1, y[n]));

    Buffer.BlockCopy(y, 0, buffer, offset, read / 4);
    return read;
    }

这是滑块的 GUI:(0% - 某些过滤器不影响信号,100% - 完全影响信号)gui 滑块

编辑 1 我重新设计了我的带通滤波器,使用 IIR chebyshev type I,order 2:

    Fc Flow Fhigh [in Hz]
50 35 71  b = [0.0050,0,-0.0050] a= [1.0000,-1.9899,0.9900]
80 57 113 b=[0.0078,0,-0.0078] a=[1.0000,-1.9843,0.9844]
130 92 184 b=[0.0127,0,-0.0127] a=[1.0000,-1.9742,0.9746]
350 247 495 b=[0.0336,0,-0.0336] a=[1.0000,-1.9305,0.9329]
1300 919 1838 b=[0.1141,0,-0.1141] a=[1.0000,-1.7414,0.7717]
4000 2828 5657 b=[0.2865,0,-0.2865] a=[1.0000,-1.1984,0.4270]
8000 5657 11314 b=[0.4559,0,-0.4559] a=[1.0000,-0.4188,0.0882]
12000 8485 16971 b=[ 0.5758,0,-0.5758] a=[1.0000,0.2477,-0.1517]
16000 11314 22040 b=[0.6532,0,-0.6532] a=[1.0000,0.6927,-0.3063]

这是我的新代码:

        private void initializeFilters()
    {
        filters = new List<Filter>();
        filters.Add(new Filter(new float[] { 0.0050f, 0f, -0.0050f }, new float[] { 1.0000f, -1.9899f, 0.9900f }));
        filters.Add(new Filter(new float[] { 0.0078f, 0f, -0.0078f }, new float[] { 1.0000f, -1.9843f, 0.9844f }));
        filters.Add(new Filter(new float[] { 0.0127f, 0f, -0.0127f }, new float[] { 1.0000f, -1.9742f, 0.9746f }));
        filters.Add(new Filter(new float[] { 0.0336f, 0f, -0.0336f }, new float[] { 1.0000f, -1.9305f, 0.9329f }));
        filters.Add(new Filter(new float[] { 0.1141f, 0f, -0.1141f }, new float[] { 1.0000f, -1.7414f, 0.7717f }));
        filters.Add(new Filter(new float[] { 0.2865f, 0f, -0.2865f }, new float[] { 1.0000f, -1.1984f, 0.4270f }));
        filters.Add(new Filter(new float[] { 0.4559f, 0f, -0.4559f }, new float[] { 1.0000f, -0.4188f, 0.0882f }));
        filters.Add(new Filter(new float[] { 0.5758f, 0f, -0.5758f }, new float[] { 1.0000f, 0.2477f, -0.1517f }));
        filters.Add(new Filter(new float[] { 0.6532f, 0f, -0.6532f }, new float[] { 1.0000f, 0.6927f, -0.3063f }));
    }
    public void process(float[] x, ref float[] y, float value, int i)
    {
        float f = ((100 * value) / 20)/100; //mapping values to 0.0 - 1.0
        float[] tmp = new float[y.Length];
        for (int n = 0; n < x.Length; n++)
        {
            for (int k = 0; k < filters[i].B.Length; k++)
            {
                if (n - k >= 0)
                {
                    tmp[n] += filters[i].B[k] * x[n - k];
                    y[n] += f * tmp[n];
                }
            }
            for (int k = 1; k < filters[i].A.Length; k++)
            {
                if (n - k >= 0)
                {
                    tmp[n] -= filters[i].A[k] * tmp[n - k];
                    y[n] -= f * tmp[n];
                }
            }
        }
    }
    public override int Read(byte[] buffer, int offset, int count)
    {
        int read = sourceStream.Read(buffer, offset, count);
        float[] f_read = new float[read / 4], y = new float[read / 4];
        Buffer.BlockCopy(buffer, offset, f_read, 0, count);

        int st = 1;
        for (int i = 0; i < Form1.trackBarValue.Length; i++)
        {
            if (Form1.trackBarValue[i] > 0)
            {
                process(f_read, ref y, Form1.trackBarValue[i], i);
                st++;
            }
        }
        if (st == 1)
           process(f_read, ref y, 20, 0); //20 is just a maximum slider value

        for (int n = 0; n < read / 4; n++)
            y[n] = Math.Min(1, Math.Max(-1, y[n] / st));

        Buffer.BlockCopy(y, 0, buffer, offset, read);
        return read;
    }

我在信号中听到一点噪音(有点刘海),谁能告诉我我做错了什么?

2个回答

一堆东​​西:

  1. 你的乐队定义真的很奇怪。一些低频段真的很小,而一些中频段很大。理想情况下,您需要频带边缘为中心频率附近的 1/sqrt(2) 和 sqrt(2) 的八度音阶
  2. 您已经并行设计了带通滤波器。这些不是“重建的”,因此如果将带通的输出加在一起,您最终不会得到平坦的频率响应。
  3. 您需要级联的“参数”或“峰值均衡器”滤波器。这些滤波器由中心频率、增益(或截止)和 Q 定义。Q 控制带宽,Q=2 应该为您提供类似于 ocatve 带宽的东西。请参阅http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
  4. 每次移动滑块时,您都必须更新滤波器系数。这可能会导致弹出和点击,并且有多种方法可以解决此问题。如果这是一个问题,请发布另一个问题

回复:编辑 1

  1. 您仍在使用并行带通,因此如果所有频带增益都是“中性”,您将永远不会获得完美的频率响应
  2. 滑块感觉不对劲。典型的图形均衡器将以对数步长执行 -15dB .. +15dB。您的滑块是线性的,它只提供剪切
  3. 你有一个“硬剪辑”来防止溢出。如果溢出真的发生,那听起来会很糟糕。有更好的方法来处理它。
  4. 我认为主要问题是您的状态管理(或缺乏状态管理)。有一个条件:if(nk>=0). 这是错误的,您总是需要对整个循环进行求和。为此,您需要保留前一个数据块的最后两个输入和输出样本。所以块 K 的 x[n-1] 变成了块 K+1 的 x[-1]。您需要跟踪 x[-1]、x[-2]、y[-1] 和 y[-2]。

级联运行 9 个五阶 IIR 滤波器可能会产生噪声问题。但是,如果您可以使用浮点数,那么也许这不是问题。然后是 MIPS 的潜在问题,运行 9 个五阶 IIR 滤波器 pr 通道并不便宜。

并行结构为上述两个问题提供了一种可行的替代方案。这篇论文“数字助听器的低延迟信号处理”给出了设计滤波器的解释。在这种并行结构中,增益直接位于双二阶的输出上。