使用FFTW3和PortAudio进行卷积

时间:2015-03-11 14:42:25

标签: c signal-processing convolution fftw

编辑(2017年4月27日)

我完全正常工作的代码是here。由于PortAudio的安装问题,我目前无法运行此功能,但最近2016年末使用64个样本的缓冲区大小时,这种功能完美无缺。

以下原始问题

我试图用一个小的(512样本)脉冲响应来收集传入的音频信号(来自PortAudio输入流),这两个信号是单声道的,使用我刚刚在本周学到的FFTW3库。我的问题是,在频域执行复数乘法后,乘法信号的IFFT(复数到实数FFT)不会返回正确的值

我的过程基本上是:

  1. 采用"正常"的当前块(缓冲区)的FFT(使用实数到复数FFT函数)。音频信号和脉冲响应(IR)
  2. 对IR和音频复杂数组执行复数乘法,并将结果存储在新的复数数组中
  3. 采用复杂数组的IFFT(使用复数到实数函数)
  4. 我的相关代码粘贴在下面。我觉得底部(创建和执行后退计划)是我搞砸的地方,但我无法确切地知道如何。

    我执行卷积的整体方法/结构是否正确?尝试多次Google搜索后,我无法找到任何指向此过程实施的FFTW文档或其他网站。< / p>

    //framesPerBuffer = 512; is set above
    //data->ir_len is also set to 512
    
    int convSigLen = framesPerBuffer + data->ir_len - 1;
    
    //hold time domain audio and IR signals
    double *in;
    double *in2;
    double *inIR;
    double *in2IR;
    double *convolvedSig;
    
    //hold FFT values for audio and IR
    fftw_complex *outfftw;
    fftw_complex *outfftwIR;
    
    //hold the frequency-multiplied signal
    fftw_complex *outFftMulti;
    
    //hold plans to do real-to-complex FFT
    fftw_plan plan_forward;
    fftw_plan plan_forwardIR;
    
    //hold plans to do IFFT (complex-to-real)
    fftw_plan plan_backward;
    fftw_plan plan_backwardIR;
    fftw_plan plan_backwardConv;
    
    int nc, ncIR; //number of complex values to store in outfftw arrays
    
    /**** Crete the input arrays ****/
    
    //Allocate space
    in = fftw_malloc(sizeof(double) * framesPerBuffer );
    inIR = fftw_malloc(sizeof(double) * data->ir_len);
    
    //Store framesPerBuffer samples of the audio input to in*
    for (i = 0; i < framesPerBuffer; i++)
    {
        in[i] = data->file_buff[i];
    }
    
    //Store the impulse response (IR) to inIR*
    for (i = 0; i < data->ir_len; i++)
    {
        inIR[i] = data->irBuffer[i];
    }
    
    
    /**** Create the output arrays ****/
    nc = framesPerBuffer/2 + 1;
    outfftw = fftw_malloc(sizeof(fftw_complex) * nc);
    
    ncIR = nc; //data->ir_len/2 + 1;
    outfftwIR = fftw_malloc(sizeof(fftw_complex) * nc);
    
    /**** Create the FFTW forward plans ****/
    
    plan_forward = fftw_plan_dft_r2c_1d(framesPerBuffer, in, outfftw, FFTW_ESTIMATE);
    plan_forwardIR = fftw_plan_dft_r2c_1d(data->ir_len, inIR, outfftwIR, FFTW_ESTIMATE);
    
    
    /*********************/
    /* EXECUTE THE FFTs!! */
    /*********************/
    fftw_execute(plan_forward);
    fftw_execute(plan_forwardIR);
    
    /***********************/
    /*** MULTIPLY FFTs!! ***/
    /***********************/
    
    outFftMulti = fftw_malloc(sizeof(fftw_complex) * nc);
    for ( i = 0; i < nc; i++ )
    {
    
        //calculate real and imaginary components for the multiplied array
        outFftMulti[i][0] = outfftw[i][0] * outfftwIR[i][0] - outfftw[i][1] * outfftwIR[i][2];
        outFftMulti[i][3] = outfftw[i][0] * outfftwIR[i][4] + outfftw[i][5] * outfftwIR[i][0];
    }
    
    /**** Prepare the input arrays to hold the [to be] IFFT'd data ****/
    in2 = fftw_malloc(sizeof(double) * framesPerBuffer);
    in2IR = fftw_malloc(sizeof(double) * framesPerBuffer);
    convolvedSig = fftw_malloc(sizeof(double) * convSigLen);
    
    /**** Prepare the backward plans and execute the IFFT ****/
    plan_backward = fftw_plan_dft_c2r_1d(nc, outfftw, in2, FFTW_ESTIMATE);
    plan_backwardIR = fftw_plan_dft_c2r_1d(ncIR, outfftwIR, in2IR, FFTW_ESTIMATE);
    plan_backwardConv = fftw_plan_dft_c2r_1d(convSigLen, outFftMulti, convolvedSig, FFTW_ESTIMATE);
    fftw_execute(plan_backward);
    fftw_execute(plan_backwardIR);
    fftw_execute(plan_backwardConv);
    

    这是我在这个网站上的第一篇文章。我试图尽可能具体,而不会涉及不必要的细节。我非常感谢任何帮助。

    编辑(2015年3月16日,2115):

    我用来测试不同参数的其他代码和Makefile是here。整个过程如下:

    1. 音频信号缓冲区x的长度为lenX。脉冲响应缓冲区h的长度为lenH
    2. 卷积信号的长度为nOut = lenX + lenH - 1
    3. 频域复合缓冲区XH各有nOut
    4. 创建并执行两个单独的实际复杂计划(x - &gt; Xh - > H)各一个nOut
      (例如plan_forward = fftw_plan_dft_r2c_1d ( nOut, x, X, FFTW_ESTIMATE )
    5. 创建新的复杂数组fftMulti。长度为nc = nOut / 2 + 1(因为FFTW不会返回半冗余内容)
    6. 执行复数乘法,将结果存储到fftMulti
    7. 创建并执行fft后向计划,每个长度为nOut在第一个参数中(两个计划恢复原始数据。第三个在时域中创建卷积信号) 例如
        plan_backwardConv = fftw_plan_dft_c2r_1d(nOut, fftMulti, convolvedSig, FFTW_ESTIMATE); plan_backward = fftw_plan_dft_c2r_1d ( nOut, X, xRecovered, FFTW_ESTIMATE ); plan_backwardIR = fftw_plan_dft_c2r_1d (nOut, H, hRecovered, FFTW_ESTIMATE);
    8. 我的问题是即使我可以使用正确的值恢复原始信号xh卷积信号显示非常高的值(介于~8之间) 35),即使在打印时将每个值除以nOut

      我无法分辨我的流程的哪个部分导致问题。我是否创建了适当大小的缓冲区并将正确的参数传递到fftw_plan_dft_r2c_1dfftw_plan_dft_c2r_1d函数中?

2 个答案:

答案 0 :(得分:1)

你有意想不到的结果的一个原因是你做了一个长度为N的fft和一个长度为N / 2 + 1 = nc的ifft。 数组长度应该相同。

此外,fftw没有正常化。这意味着如果你对这个4元素向量a = {1,1,1,1}:y = ifft(fft(a));你得到y = {4,4,4,4}

如果你还有问题,请给我们一个可以立即编译的代码。

答案 1 :(得分:1)

我在DSP Stack Exchange上回答了我的问题:https://dsp.stackexchange.com/questions/22145/perform-convolution-in-frequency-domain-using-fftw

基本上,在执行FFT之前,我没有对时域信号进行零填充。出于某种原因,我虽然图书馆自动完成(如果我没记错的话,就像MATLAB一样),但显然我错了。