音频信号的卷积

时间:2014-08-30 09:29:40

标签: android processing audio-recording convolution

我正在使用audiorecorder录制声音并在Android手机上进行伪时间处理。 我在FFT和音频信号卷积之间遇到问题: 我对已知信号(正弦波形)执行FFT,并且通过使用FFT,我正确地总是找到其中包含的单音。

现在我想通过使用卷积(它是一个练习)来做同样的事情:我使用5000个过滤器对该信号执行5000次卷积。每个滤波器是0到5000 Hz之间不同频率的正弦波形。 然后,我搜索每个卷积输出的峰值。通过这种方式,当我使用信号中包含相同音调的滤波器时,我应该找到最大峰值。

实际上,2kHz的音调可以通过2kHz滤波器找到最大值。

问题是,当我收到4kHz音调时,我发现使用4200Hz滤波器进行卷积时的最大值(而FFT总是工作正常) 它是可能的吗? 我的卷积有什么问题?

这是我写的卷积函数:

 //i do the convolution and return the max
 //IN is the array with the signal
 //DATASIZE is the size of the array IN
 //KERNEL is the filter containing the sine at the selected frequency

 int convolveAndGetPeak(short[] in,int dataSize, double[] kernel) {
        //per non rischiare l'overflow, il kernel deve avere un ampiezza massima pari a 1/10 del max
        int i, j, k;
        int kernelSize=kernel.length;
        int tmpSignalAfterFilter=0;
        double out;

        // convolution from out[0] to out[kernelSize-2]
        //iniziamo 
        for(i=0; i < kernelSize - 1; ++i)
        {
            out = 0; // init to 0 before sum

            for(j = i, k = 0; j >= 0; --j, ++k)
                out += in[j] * kernel[k];

            if (Math.abs((int) out)>tmpSignalAfterFilter ){
                tmpSignalAfterFilter=Math.abs((int) out);   
            }
        }

        // start convolution from out[kernelSize-1] to out[dataSize-1] (last)
        //iniziamo da dove eravamo arrivati
        for( ; i < dataSize; ++i)
        {
            out = 0;  // initialize to 0 before accumulate

            for(j = i, k = 0; k < kernelSize; --j, ++k)
                out += in[j] * kernel[k];

            if (Math.abs((int) out)>tmpSignalAfterFilter ){
                tmpSignalAfterFilter=Math.abs((int) out);   
            }

        }


        return tmpSignalAfterFilter;
    }

用作过滤器的内核以这种方式生成:

 //curFreq is the frequency of the filter in Hz
 //kernelSamplesSize is the desired length of the filter (number of samples), for time precision reasons i'm using 20 samples length.
 //sampleRate is the sampling frequency

 double[] generateKernel(int curFreq,int kernelSamplesSize,int sampleRate){
    double[] curKernel= new double[kernelSamplesSize] ;

    for (int kernelIndex=0;kernelIndex<curKernel.length;kernelIndex++){
        curKernel[kernelIndex]=Math.sin( (double)kernelIndex * ((double)(2*Math.PI) * (double)curFreq / (double)sampleRate));    //the part that makes this a sine wave....
    }
    return curKernel;

 }

如果你想尝试卷积,IN数组中包含的数据如下: http://www.tr3ma.com/Dati/signal.txt

注1:采样频率为44100Hz

注2:信号中包含的音调为单个4kHz音调(即使卷积具有4200Hz滤波器的最大峰值。

编辑:我也在excel表上重复了测试。结果是一样的(当然,我使用相同的算法),算法在我看来是正确的... 这是我准备的excel表,如果您更喜欢使用excel:http://www.tr3ma.com/Dati/convolutions.xlsm

2 个答案:

答案 0 :(得分:1)

您可以通过两个因素更改带宽:

a)内核的长度(例如,长度t为5ms产生f> = 200Hz的粗带宽,估计为1 / 0.005,因为Δt·Δf> = 1,参见“Heisenberg”),和< / p> b)窗口函数(你必须实现它才能使你的算法在实际应用程序中工作,否则在某些情况下,某些滤波器输出的旁瓣会产生比预期滤波器输出的主瓣更多的能量)。 / p>

但是你有另一个问题:你需要与由余弦波组成的第二个内核进行卷积(这意味着你需要与第一个内核相同的波,但是需要移动90度)。这是为什么?因为只有正弦内核,你会得到滤波器输出的相位相关调制(例如,如果输入信号和具有相同频率的内核波之间的相位差为90度,则得到幅度为0)。

最后,将两个内核的输出与毕达哥拉斯结合起来。

答案 1 :(得分:0)

除了内核(过滤器)的样本数量外,它似乎都是正确的。 增加过滤器的大小会使结果更准确。 我不知道如何计算此过滤器的带宽,但我觉得这是过滤器带宽的问题。因此,滤波器带宽还取决于卷积中使用的滤波器的样本数量,参考采样频率(并且还可以参考音调频率)。不幸的是,我无法增加过滤器样本的数量,因为否则手机无法实时进行过滤。 注意:我需要卷积,因为我需要确定音调被发射时的精确时刻。

编辑:我对20个样本的过滤器和40个样本的过滤器进行了比较。 我不知道获得fitler带宽的公式,但在下图中,两个过滤器之间的区别很清楚。

EDIT2:发布解决方案后的几天我发现如何计算这种过滤器的带宽:它只是过滤器持续时间的逆转。例如,在44100KhZ的40个样本的核心持续时间大约为907uS,然后滤波器带宽,这个内核和相同长度的窗口是1 / 907uS = 1,1KhZ http://www.tr3ma.com/Dati/convolution.JPG