使用fftw 3在C ++中实现卷积

时间:2017-11-20 23:00:08

标签: convolution fftw libsndfile

更新

请参阅我关于DSP stackexchange的基本问题here

更新

我仍然在输出中遇到噼啪声。这些裂纹现在不太明显,只有在音量调高时才能听到

更新

根据给出的建议here已从我的输出中删除了噼里啪啦的声音。我将使用其他可用的HRIR进行测试,以确定卷积是否确实正常工作,并且在我验证我的代码现在可以正常工作后将回答此问题

更新

我取得了一些进展,但我仍然认为我的卷积实施存在问题。 以下是我修订的计划:

#define HRIR_LENGTH 512
#define WAV_SAMPLE_SIZE 256

    while (signal_input_wav.read(&signal_input_buffer[0], WAV_SAMPLE_SIZE) >= WAV_SAMPLE_SIZE)
    {
#ifdef SKIP_CONVOLUTION
        // Copy the input buffer over
        std::copy(signal_input_buffer.begin(),
                  signal_input_buffer.begin() + WAV_SAMPLE_SIZE,
                  signal_output_buffer.begin());

        signal_output_wav.write(&signal_output_buffer[0], WAV_SAMPLE_SIZE);
#else
        // Copy the first segment into the buffer
        // with zero padding
        for (int i = 0; i < HRIR_LENGTH; ++i)
        {
            if (i < WAV_SAMPLE_SIZE)
            {
                signal_buffer_fft_in[i] = signal_input_buffer[i];
            }
            else
            {
                signal_buffer_fft_in[i] = 0; // zero pad
            }
        }

        // Dft of the signal segment
        fftw_execute(signal_fft);

        // Convolve in the frequency domain by multiplying filter kernel with dft signal
        for (int i = 0; i < HRIR_LENGTH; ++i)
        {
            signal_buffer_ifft_in[i] = signal_buffer_fft_out[i] * left_hrir_fft_out[i]
                - signal_buffer_fft_out[HRIR_LENGTH - i] * left_hrir_fft_out[HRIR_LENGTH - i];

            signal_buffer_ifft_in[HRIR_LENGTH - i] = signal_buffer_fft_out[i] * left_hrir_fft_out[HRIR_LENGTH - i]
                + signal_buffer_fft_out[HRIR_LENGTH - i] * left_hrir_fft_out[i];

            //double re = signal_buffer_out[i];
            //double im = signal_buffer_out[BLOCK_OUTPUT_SIZE - i];
        }

        // inverse dft back to time domain
        fftw_execute(signal_ifft);

        // Normalize the data
        for (int i = 0; i < HRIR_LENGTH; ++i)
        {
            signal_buffer_ifft_out[i] = signal_buffer_ifft_out[i] / HRIR_LENGTH;
        }

        // Overlap-add method
        for (int i = 0; i < HRIR_LENGTH; ++i)
        {
            if (i < WAV_SAMPLE_SIZE)
            {
                signal_output_buffer[i] = signal_overlap_buffer[i] + signal_buffer_ifft_out[i];
            }
            else
            {
                signal_output_buffer[i] = signal_buffer_ifft_out[i];
                signal_overlap_buffer[i] = signal_output_buffer[i]; // record into the overlap buffer
            }
        }

        // Write the block to the output file
        signal_output_wav.write(&signal_output_buffer[0], HRIR_LENGTH);

#endif
    }

生成的输出声音文件包含噼啪声;据推测,人工制品是从越野车的实施中留下的。另外,写入512(HRIR_LENGTH)的块似乎会导致一些混叠,播放时的声音文件听起来像黑胶唱片一样慢。写出大小为WAV_SAMPLE_SIZE(256,fft输出的一半)的块似乎以正常速度播放。 然而,无论如何,噼里啪啦的声音仍然存在。

ORIGINAL

我试图在C ++中使用fftw库实现卷积。 我可以非常精确地加载我的滤波器,并且对滤波器(长度为512)和输入信号(长度为513)进行零填充,以获得1024的信号输出块并将其用作fft大小。

这是我的代码:

#define BLOCK_OUTPUT_SIZE 1024
#define HRIR_LENGTH 512

#define WAV_SAMPLE_SIZE 513
#define INPUT_SHIFT 511

while (signal_input_wav.read(&signal_input_buffer[0], WAV_SAMPLE_SIZE) >= WAV_SAMPLE_SIZE)
{
#ifdef SKIP_CONVOLUTION
    // Copy the input buffer over
    std::copy(signal_input_buffer.begin(),
              signal_input_buffer.begin() + WAV_SAMPLE_SIZE,
              signal_output_buffer.begin());

    signal_output_wav.write(&signal_output_buffer[0], WAV_SAMPLE_SIZE);
#else
    // Zero pad input
    for (int i = 0; i < INPUT_SHIFT; ++i)
        signal_input_buffer[WAV_SAMPLE_SIZE + i] = 0;

    // Copy to the signal convolve buffer
    for (int i = 0; i < BLOCK_OUTPUT_SIZE; ++i)
    {
        signal_buffer_in[i] = signal_input_buffer[i];
    }

    // Dft of the signal segment
    fftw_execute(signal_fft);

    // Convolve in the frequency domain by multiplying filter kernel with dft signal
    for (int i = 1; i < BLOCK_OUTPUT_SIZE; ++i)
    {
        signal_buffer_out[i] = signal_buffer_in[i] * left_hrir_fft_in[i]
            - signal_buffer_in[BLOCK_OUTPUT_SIZE - i] * left_hrir_fft_in[BLOCK_OUTPUT_SIZE - i];

        signal_buffer_out[BLOCK_OUTPUT_SIZE - i]
            = signal_buffer_in[BLOCK_OUTPUT_SIZE - i] * left_hrir_fft_in[i]
                    + signal_buffer_in[i] * left_hrir_fft_in[BLOCK_OUTPUT_SIZE - i];

        double re = signal_buffer_out[i];
        double im = signal_buffer_out[BLOCK_OUTPUT_SIZE - i];
    }

    // inverse dft back to time domain
    fftw_execute(signal_ifft);

    // Normalize the data
    for (int i = 0; i < BLOCK_OUTPUT_SIZE; ++i)
    {
        signal_buffer_out[i] = signal_buffer_out[i] / i;
    }

    // Overlap and add with the previous block
    if (first_block)
    {
        first_block = !first_block;
        for (int i = 0; i < BLOCK_OUTPUT_SIZE; ++i)
        {
            signal_output_buffer[i] = signal_buffer_out[i];
        }
    }
    else
    {
        for (int i = WAV_SAMPLE_SIZE; i < BLOCK_OUTPUT_SIZE; ++i)
        {
            signal_output_buffer[i] = signal_output_buffer[i] + signal_buffer_out[i];
        }
    }

    // Write the block to the output file
    signal_output_wav.write(&signal_output_buffer[0], BLOCK_OUTPUT_SIZE);
#endif
}

最后,生成的输出文件包含垃圾,但不是全部为零。 我尝试过的事情:

1)使用标准复杂接口 fftw_plan_dft_1d 和适当的fftw_complex类型。出现同样的问题。

2)使用较小的输入样本大小并迭代零填充块(重叠添加)。

我还注意到它不是libsndfile的错;切换 SKIP_CONVOLUTION 会成功将输入文件复制到输出文件。

0 个答案:

没有答案