Я не получаю ожидаемых результатов от свертки с добавлением перекрытия FFT с использованием FFTW - PullRequest
2 голосов
/ 24 апреля 2019

Я пытаюсь выполнить свертку двух массивов через FFT, используя библиотеку FFTW в C ++ и реализуя алгоритм наложения-добавления.Векторные данные, в частности, состоят из двойников, загруженных из WAV-файлов с использованием sndfile - один из них представляет собой WAV-файл для обработки, другой - WAV-файл с импульсным ответом (IR), поэтому по сути целью является Convolution Reverb.

Ранее я успешно реализовал это с Python, но это включало использование FFT-свертки через scipy.signal, поэтому мне не пришлось реализовывать перекрытие-добавление самостоятельно.Но из этого опыта я могу подтвердить, что я работаю с импульсным откликом, который должен аккуратно сворачиваться с входным сигналом через БПФ и давать узнаваемый эффект эха, сравнимый с выходом Python.Я также могу подтвердить, что 2 файла WAV имеют совпадающие битрейты (16 бит) и частоты дискретизации (44,1 кГц).

Несколько фальстартов приводили только к плоскому шуму на выходе, знакомый камень преткновения.Теперь я получаю вывод WAV, который почти напоминает меняющиеся ответвления импульсного отклика, но только с импульсами шума на каждом воспринимаемом «ответвлении».

Я следую двум руководствам по моей реализации add-overlap-add;https://www.dspguide.com/ch18/1.htm

http://blog.robertelder.org/overlap-add-overlap-save/

Я также слежу за некоторыми онлайн-примерами и другими темами StackOverflow и читаю официальные документы FFTW (http://www.fftw.org/fftw2_doc/fftw_2.html).

Вот мойкод, процесс которого объяснен ниже:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sndfile.h>
#include <iostream>
#include <cstring>
#include <vector>
#include <complex.h>
#include <fftw3.h>

using namespace std;

#define ARRAY_LEN(x)    ((int) (sizeof (x) / sizeof (x [0])))
#define MAX(x,y)        ((x) > (y) ? (x) : (y))
#define MIN(x,y)        ((x) < (y) ? (x) : (y))

fftw_complex* fftw_complex_transform(vector<double >);
fftw_complex* ifft_from_complex_vector(vector<vector<double> >);
vector<double> convolved_block(vector<double>, vector<complex<double> >, int);
vector<complex<double> > filter_kernel_cpx_vec(vector<double>);
vector<double> padded(vector<double>, int);
void testDummyData();

int main (int argc, char ** argv) {
    SNDFILE *infile, *outfile, *irfile ;
    SF_INFO sfinfo ;
    double buffer[1024];
    sf_count_t count;

    // 1 - import both WAV files 
    vector<double> inputWavArr;
    vector<double> irWavArr;

    if (argc != 4) {
        printf("\nUsage :\n\n    <executable name>  <input signal file> <impulse response file> <output file>\n") ;
        exit(0);
    }

    memset (&sfinfo, 0, sizeof (sfinfo)) ;
    if ((infile = sf_open (argv [1], SFM_READ, &sfinfo)) == NULL)     {     
        printf ("Error : Not able to open input file '%s'\n", argv [1]);
        sf_close (infile);
        exit (1) ;
    } 

    if ((irfile = sf_open (argv [2], SFM_READ, &sfinfo)) == NULL)     {     
        printf ("Error : Not able to open input file '%s'\n", argv [2]);
        sf_close (irfile);
        exit (1) ;
    }   

    if ((outfile = sf_open (argv [3], SFM_WRITE, &sfinfo)) == NULL) { 
        printf ("Error : Not able to open output file '%s'\n", argv [3]);
        sf_close (outfile);
        exit (1);
    }

    while ((count = sf_read_double (infile, buffer, ARRAY_LEN (buffer))) > 0) {
        for (int i = 0; i < 1024; i++)
            inputWavArr.push_back(buffer[i]);
    }
    while ((count = sf_read_double (irfile, buffer, ARRAY_LEN (buffer))) > 0) {
        for (int i = 0; i < 1024; i++)
            irWavArr.push_back(buffer[i]); // max value 0.0408325
    }
    // 2 - Settle on a padding scheme
    int irLen = irWavArr.size();
    int windowSize = 262144 - irLen+1; //  262144 is a pwr of 2
    const int outputLength = irLen + windowSize - 1;

    sf_close(infile);
    sf_close(irfile);

    irWavArr = padded(irWavArr, outputLength);
    int newIrLength = irWavArr.size();
    // 3 and 4 - use FFTW to process IR kernel into vector of complex values
    vector<complex<double> > ir_vec;
    ir_vec = filter_kernel_cpx_vec(irWavArr);
    // 5 - divide dry input signal into sections of length windowSize
    int numSections = floor(inputWavArr.size()/windowSize);

    // OVERLAP-ADD PROCEDURE
    vector<vector<double> > totals;
    cout << "numSections is " << numSections << "\n";

    for (int j=0; j<numSections; j++) { // may be OBOB use numSections+1? or pad inputWavArr? 
        vector<double> total;
        cout << "convolving section " << j << "\n";
        vector<double> section_arr;
        for (int i=j*windowSize; i<(j*windowSize + windowSize); i++) {
            section_arr.push_back(inputWavArr[i]);
        }

        // Hanning window
        for (int i = 0; i < windowSize; i++) {
            double multiplier = 0.5 * (1 - cos(2*M_PI*i/(windowSize-1)));
            section_arr[i] = multiplier * section_arr[i];
        }
        int old_section_arr_size = section_arr.size();
        section_arr = padded(section_arr, outputLength);
        // 6 - yield convolved block for each section
        vector<double> output = convolved_block(section_arr, ir_vec, old_section_arr_size+irLen-1);

        for (int i=0; i<output.size(); i++) {
            total.push_back(output[i]);
        }
        // 7 - append convolved block to vector of resultant block vectors
        totals.push_back(total);
    }
    vector<double> results(inputWavArr.size()+newIrLength-1, 0);
    // 8 - loop though vector of convolved segments, and carry out addition of overlapping sub-segments to yield final "results" vector
    for (int j=0; j<numSections; j++) {
        vector<double> totals_arr = totals[j];
        cout << "overlap summing section " << j << "\n";
        for (int i=0; i<totals_arr.size(); i++) {
            int newIdx = j*windowSize+i;
            results[newIdx] += totals_arr[i];
        }
    }
    // RESULTS MARK THE END OF OVERLAP-ADD PROCEDURE

    // load result vector into buffer for writing via libsndfile
    double* buff3 = (double*)malloc(results.size()*sizeof(double));
    for (int idx = 0; idx < results.size(); idx++) {
        buff3[idx] = results[idx]/400; // normalizing factor for these samples... output without this has amplitude going almost up to 120. input file had max around 0.15. max should be 1 about
    }

    long writtenFrames = sf_writef_double (outfile, buff3, results.size());
    sf_close (outfile);

    return 0 ;
}

fftw_complex* fftw_complex_transform(vector<double> signal_wav) {
    int N = signal_wav.size();
    fftw_complex *in, *out;
    fftw_plan irPlan;
    in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);
    out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);

    for (int i = 0; i < signal_wav.size(); i++)
    {
        in[i][0] = signal_wav[i];
        in[i][1] = (float)0; // complex component .. 0 for input of wav file
    }
    irPlan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
    fftw_execute(irPlan);
    fftw_destroy_plan(irPlan);
    fftw_free(in);

    return out;
}

vector<complex<double> > filter_kernel_cpx_vec(vector<double> input) {
    fftw_complex* irFFT = fftw_complex_transform(input);

    vector<complex<double> > kernel_vec;
    for (int i=0; i<input.size(); i++) {
        complex<double> m1 (irFFT[i][0], irFFT[i][1]);
        kernel_vec.push_back(m1);
    }

    fftw_free(irFFT); 
    return kernel_vec;
}

fftw_complex* ifft_from_complex_vector(vector<vector<double> > signal_vec) {
    int N = signal_vec.size();
    fftw_complex *in, *out;
    fftw_plan irPlan;
    in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);
    out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);

    for (int i = 0; i < signal_vec.size(); i++)
    {
        in[i][0] = signal_vec[i][0]; // real
        in[i][1] = signal_vec[i][1]; // imag
    }
    irPlan = fftw_plan_dft_1d(N, in, out, FFTW_BACKWARD, FFTW_ESTIMATE);
    fftw_execute(irPlan);
    fftw_destroy_plan(irPlan);
    fftw_free(in);

    return out;
}

vector<double> convolved_block(vector<double> in_vector, vector<complex<double> > ir_cpx_vector, int size) {
    fftw_complex* outputFFT = fftw_complex_transform(in_vector);

    vector<vector<double> > products;
    vector<complex<double> > sig_fft_cpx;
    for (int i=0; i<size; i++) {
        complex<double> m1 (outputFFT[i][0], outputFFT[i][1]);
        sig_fft_cpx.push_back(m1);
    }        
    fftw_free(outputFFT);
    for (int j=0; j<size; j++) {
        std::complex<double> complexProduct = sig_fft_cpx[j]*ir_cpx_vector[j];
        double re = real(complexProduct);
        double im = imag(complexProduct);
        vector<double> elemVec(2);
        elemVec[0] = re;
        elemVec[1] = im;

        products.push_back(elemVec);
    }
    fftw_complex* revFFT = ifft_from_complex_vector(products);
    vector<double> out_vec_dbl;

    for (int i=0; i<size; i++) {
        out_vec_dbl.push_back(outputFFT[i][0]);
    }
    fftw_free(revFFT);
    return out_vec_dbl;
}

vector<double> padded(vector<double> input, int total) {
    vector<double> padded_vec;
    for (int i = 0; i<input.size(); i++) {
    padded_vec.push_back(input[i]);
    }
    int numZeroes = total - input.size();
    for (int i = 0; i< numZeroes; i++) {
    padded_vec.push_back((double)0);
    }
    return padded_vec;
}

void testDummyData() {
    vector<double> dummyFilter;
    dummyFilter.push_back(1);
    dummyFilter.push_back(-1);
    dummyFilter.push_back(1);

    vector<double> dummySignal;
    dummySignal.push_back(3);
    dummySignal.push_back(-1);
    dummySignal.push_back(0);
    dummySignal.push_back(3);
    dummySignal.push_back(2);
    dummySignal.push_back(0);
    dummySignal.push_back(1);
    dummySignal.push_back(2);
    dummySignal.push_back(1);

    const int nearWindowSize=3;
    const int nearIrLength=3;
    const int totalLength = 5;

    dummyFilter = padded(dummyFilter, totalLength);
    vector<complex<double> > dummy_ir_vec = filter_kernel_cpx_vec(dummyFilter);

    int localNumSections = 3;
    vector<vector<double> > outputs;
    for (int j=0; j<localNumSections; j++) {
        vector<double> local_section;
        for (int i; i<nearWindowSize; i++) {
            int idx = j*nearWindowSize + i;
            local_section.push_back(dummySignal[idx]);
        }
        local_section = padded(local_section, totalLength);
        vector<double> local_output = convolved_block(local_section, dummy_ir_vec, totalLength);
        outputs.push_back(local_output);
    }
    vector<double> local_results(11,0); // example has 11 in output

    for (int j=0; j<localNumSections; j++) {
        vector<double> local_totals_arr = outputs[j];
        cout << "overlap summing section " << j << "\n";
        for (int i=0; i<local_totals_arr.size(); i++) {
            int newIdx = j*nearWindowSize+i;
            local_results[newIdx] += local_totals_arr[i];
        }
    }    
    for (int i=0; i<11; i++) {
        cout << "result " << i << "\n";
        cout << local_results[i] << "\n";
    }
}

Мой процесс:

  1. импортировать оба файла WAV («сухой» входной сигнал и ИК) через libsndfile, получая векторные представления,которые, по-видимому, хорошо декодируются libsndfile из WAV через PCM.
  2. основаны на схеме заполнения. Я стремился к ИК-излучению и сегментам «сухого» входного сигнала для каждого из них дополняли до общей длины 262144 выборок., степень двух (2 ^ 18), как рекомендовано в различных руководствах.Так как я импортирую импульсную характеристику с длиной 189440 выборок, это даст размер окна сегмента 72705, так что оба сегмента векторов сухого сигналаи ИК-вектор может быть дополнен до общей длины 2 ^ 18 выборок.
  3. использует FFTW (в частности, функцию fftw_plan_dft_1d в режиме FFTW_FORWARD) для обработки вектора спредставление во временной области IR N выборок (включая заполнение, то есть 2 ^ 18 выборок), причем FFTW дает указатель на (предполагаемый) список N-длин комплексных векторов для описания частотной области IR.
  4. обработать этот указатель в векторном списке сложных векторных значений, чтобы представить ядро ​​ИК-фильтра в формате, который мне больше всего понадобится для обработки сегментов входного сигнала.закройте план FFTW и освободите выходной указатель FFTW после загрузки этого вектора.
  5. разделите сухой сигнал wav на сегменты 72705 выборок с заполнением нулями до общей длины 262144 выборок для каждого сегмента, включая перекрытиеобласть, край.Используйте процедуру окна Ханнинга (умножение на значения косинуса с периодом, зависящим от размера окна) перед заполнением, чтобы исключить возможное наложение псевдонимов от высокочастотных артефактов по краям окон.
  6. для каждого дополненного сегмента сухогосигнал, создайте «свернутый блок», вызывая convolved_block с этим сегментом, а также комплексный вектор IR в качестве аргументов.
    1. «convolved_block» работает следующим образом - используйте FFTW (опять же, fftw_plan_dft_1d в режиме FFTW_FORWARD), чтобы запустить FFT на векторе сухого сегмента («in_vector«) и получить вектор комплексных значений, а также умножить элементыэтот комплексный вектор, поэлементный, с комплексным векторным представлением ядра IR (ir_cpx_vector).Это сложное умножение, которое обрабатывается классом std :: complex.
    2. Запустите обратное БПФ для результирующего массива комплексных векторов (опять-таки это fftw_plan_dft_1d, но теперь в режиме FFTW_: BACKWARD).
    3. Используйте действительные компоненты полученного массива N-длины, чтобы получить N значений амплитуды выборки для этого свернутого перекрывающегося блока
  7. Добавить каждое разрешениеВычисление «свернутого блока» в наш вектор свернутых перекрывающихся блоков (вектор> итоги).
  8. Выполните цикл по этому вектору результатов (итоги ") и выполните наложение-сложение, чтобы получить новый вектор, немного более длинный, чем исходный сухой сигнал (результаты вектора). После суммирования это должно представлять собой свернутый сигнал, который затем может быть помещается в буфер и экспортируется в WAV через libsndfile.

Так как это не дало ожидаемых результатов, я попытался запустить те же функции FFT более высокого уровня (те, которые инкапсулируют вызовы FFTW) на демонстрационных данных из руководства, на которое я ссылался выше (http://blog.robertelder.org/overlap-add-overlap-save/).

)

Этот тест реализован с помощью функции void testDummyData ().

В этом тесте промежуточные комплексные значения БПФ не были такими же, как в руководстве, и не были конечными результатами «свернутых» векторов (подтвержденных как правильные в руководстве с помощью алгоритма входной стороны для небольшого набора данных). Это заставляет меня чесать голову, так как я внимательно следил за учебником FFTW (http://www.fftw.org/fftw2_doc/fftw_2.html), чтобы получить результаты FFT. Я дважды проверил свою реализацию add-overlap-add, и я постарался избежать распространенных ошибок реализации, таких как отсутствие окна Хеннинга или неправильная схема заполнения (на этом этапе кажется, что заполнение является правильным…). Очевидно, что-то здесь я упускаю.

Одно замечание на выходе: «свернутые» выходные выборки имеют намного большую амплитуду, чем максимальные амплитуды выборок для сухого входного сигнала или ИК-сигнала. Я использовал деление в 400 раз для получения приблизительно нормализованного диапазона громкости, хотя, конечно, это не решает фундаментальную проблему. Тем не менее, я надеюсь, что эта неожиданная амплитуда дает некоторое представление о проблеме.

Подробности среды: скрипт C ++, если он сохранен как convolution.cpp, может быть скомпилирован с помощью следующей команды: g ++ pkg-config --cflags --libs fftw3 sndfile convolution.cpp -o outprog Для работы требуются только библиотеки fftw и libsndfile, и я установил обе версии с homebrew на Mac. И его можно запустить следующим образом, вот как я загрузил соответствующие файлы (так же, как в рабочей версии Python): ./outprog ../F.wav ../spaceEchoIR.wav wetSignal.wav

1 Ответ

1 голос
/ 25 апреля 2019

Похоже, я решил основную проблему.На revFFT, указатель, возвращенный из функции-оболочки inverse-fft (ifft_from_complex_vector), не было ссылки после загрузки значений.Вместо этого код искал ожидаемые значения в outputFFT, который уже был освобожден с помощью fftw_free.

Еще одна вещь, которая помогла устранить недоразумение, заключалась в переключении на функции FFTW fftw_plan_dft_r2c_1d и fftw_plan_dft_c2r_1d, чтобы заменить вызовы fftw_plan_используя флаги FFTW_BACKWARD и FFTW_FORWARD.Типы теперь намного легче отслеживать, особенно для векторов действительных чисел, которые должны работать с FFTW.Есть некоторые окна и отдельные части, которые испытывают искажения и искажения.Вероятно, это связано с моей постоянной потребностью в надежной схеме нормализации, как это предложил Крис.Это то, над чем я работаю дальше, но, к счастью, основная проблема решена.

Рабочий код ниже:

//Give the input and output file names on the command line
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sndfile.h>
#include <iostream>
#include <cstring>
#include <vector>
#include <complex.h>
#include <fftw3.h>

using namespace std;

#define ARRAY_LEN(x)    ((int) (sizeof (x) / sizeof (x [0])))
#define MAX(x,y)        ((x) > (y) ? (x) : (y))
#define MIN(x,y)        ((x) < (y) ? (x) : (y))

fftw_complex* fftw_complex_transform(vector<double >);
//fftw_complex* ifft_from_complex_vector(vector<vector<double> >);
double* ifft_from_complex_vector(vector<vector<double> >);
vector<double> convolved_block(vector<double>, vector<complex<double> >, int);
vector<complex<double> > filter_kernel_cpx_vec(vector<double>);
vector<double> padded(vector<double>, int);
void testDummyData();

int main (int argc, char ** argv) {
    SNDFILE *infile, *outfile, *irfile ;
    SF_INFO sfinfo ;
    double buffer[1024];
    sf_count_t count;

    //for fftw3
    vector<double> inputWavArr;
    vector<double> irWavArr;

    if (argc != 4) {
        printf("\nUsage :\n\n    <executable name>  <input signal file> <impulse response file> <output file>\n") ;
        exit(0);
    }

    memset (&sfinfo, 0, sizeof (sfinfo)) ;
    if ((infile = sf_open (argv [1], SFM_READ, &sfinfo)) == NULL)     {     
        printf ("Error : Not able to open input file '%s'\n", argv [1]);
        sf_close (infile);
        exit (1) ;
    } 

    if ((irfile = sf_open (argv [2], SFM_READ, &sfinfo)) == NULL)     {     
        printf ("Error : Not able to open input file '%s'\n", argv [2]);
        sf_close (irfile);
        exit (1) ;
    }   

    if ((outfile = sf_open (argv [3], SFM_WRITE, &sfinfo)) == NULL) { 
        printf ("Error : Not able to open output file '%s'\n", argv [3]);
        sf_close (outfile);
        exit (1);
    }

    while ((count = sf_read_double (infile, buffer, ARRAY_LEN (buffer))) > 0) {
        for (int i = 0; i < 1024; i++)
            inputWavArr.push_back(buffer[i]);
    }
    double sumIrImpulses = 0;
    while ((count = sf_read_double (irfile, buffer, ARRAY_LEN (buffer))) > 0) {
        for (int i = 0; i < 1024; i++) {
            double el = buffer[i];
            irWavArr.push_back(el); // max value 0.0408325
            sumIrImpulses += (el);
        }
    }
    sumIrImpulses = abs(sumIrImpulses);
    //const int irLen = 189440; // filter(ir) block len
    int irLen = irWavArr.size();
    //const int windowSize = 72705; // s.t. irLen+windowSize-1 is a pwr of 2
    int windowSize = 262144 - irLen+1; //  262144 is a pwr of 2
    const int outputLength = irLen + windowSize - 1;

    sf_close(infile);
    sf_close(irfile);

    irWavArr = padded(irWavArr, outputLength);
    int newIrLength = irWavArr.size();

    vector<complex<double> > ir_vec;
    ir_vec = filter_kernel_cpx_vec(irWavArr);

    int numSections = floor(inputWavArr.size()/windowSize);
    if (numSections*windowSize != inputWavArr.size()) {
        inputWavArr = padded(inputWavArr, (numSections+1)*windowSize);
        numSections++;
    }



    // OVERLAP-ADD PROCEDURE
    vector<vector<double> > totals;
    cout << "numSections is " << numSections << "\n";

    for (int j=0; j<numSections; j++) { // may be OBOB use numSections+1? or pad inputWavArr? 
        vector<double> total;
        cout << "convolving section " << j << "\n";
        vector<double> section_arr;
        for (int i=j*windowSize; i<(j*windowSize + windowSize); i++) {
            section_arr.push_back(inputWavArr[i]/200.21);
        }

        // hanning window
        for (int i = 0; i < windowSize; i++) {
            double multiplier = 0.5 * (1 - cos(2*M_PI*i/(windowSize-1)));
            section_arr[i] = multiplier * section_arr[i];
        }
        int old_section_arr_size = section_arr.size();
        section_arr = padded(section_arr, outputLength);
        vector<double> output = convolved_block(section_arr, ir_vec, old_section_arr_size+irLen-1);

        for (int i=0; i<output.size(); i++) {
            total.push_back(output[i]); // normalize
        }
        totals.push_back(total);
    }
    vector<double> results(inputWavArr.size()+newIrLength-1, 0);

    for (int j=0; j<numSections; j++) {
        vector<double> totals_arr = totals[j];
        cout << "overlap summing section " << j << "\n";
        for (int i=0; i<totals_arr.size(); i++) {
            int newIdx = j*windowSize+i;
            results[newIdx] += totals_arr[i]/550;
        }
    }
    double maxVal = 0;
    for (int i=0; i<results.size(); i++) {
        if (results[i] > maxVal) {
            maxVal = results[i];
        }
    }
    cout << "maxval" << maxVal << "\n";
    cout << "sumIrImpulses" << sumIrImpulses << "\n";
    // RESULTS MARK THE END OF OVERLAP-ADD PROCEDURE
    double* buff3 = (double*)malloc(results.size()*sizeof(double));
    for (int idx = 0; idx < results.size(); idx++) {
        buff3[idx] = results[idx]; // NO LONGER normalizing factor for these samples... output without this has amplitude going almost up to 120. input file had max around 0.15. max should be 1 about
    }

    long writtenFrames = sf_writef_double (outfile, buff3, results.size());
    sf_close (outfile);

    return 0 ;
}

fftw_complex* fftw_complex_transform(vector<double> signal_wav) {
    int N = signal_wav.size();
    double *in;
    fftw_complex *out;
    fftw_plan irPlan;
    //in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);
    in = (double*) malloc(sizeof(double)*N);
    out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);

    for (int i = 0; i < signal_wav.size(); i++)
    {
        //in[i][0] = signal_wav[i];
        in[i] = signal_wav[i];
        //in[i][1] = (float)0; // complex component .. 0 for input of wav file
    }
    //irPlan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
    irPlan = fftw_plan_dft_r2c_1d(N, in, out, FFTW_ESTIMATE);
    fftw_execute(irPlan);
    fftw_destroy_plan(irPlan);
    fftw_free(in);

    return out;
}

vector<complex<double> > filter_kernel_cpx_vec(vector<double> input) {
    fftw_complex* irFFT = fftw_complex_transform(input);

    vector<complex<double> > kernel_vec;
    for (int i=0; i<input.size(); i++) {
        complex<double> m1 (irFFT[i][0], irFFT[i][1]);
        kernel_vec.push_back(m1);
    }

    fftw_free(irFFT); 
    return kernel_vec;
}

//fftw_complex* ifft_from_complex_vector(vector<vector<double> > signal_vec) {
double* ifft_from_complex_vector(vector<vector<double> > signal_vec) {
    int N = signal_vec.size();
    //fftw_complex *in, *out;
    fftw_complex *in;
    double *out;
    fftw_plan irPlan;
    in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);
    //out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);
    out = (double*) malloc(sizeof(double)*N);

    for (int i = 0; i < signal_vec.size(); i++)
    {
        in[i][0] = signal_vec[i][0]; // real
        in[i][1] = signal_vec[i][1]; // imag
    }
    //irPlan = fftw_plan_dft_1d(N, in, out, FFTW_BACKWARD, FFTW_ESTIMATE);
    irPlan = fftw_plan_dft_c2r_1d(N, in, out, FFTW_ESTIMATE);
    fftw_execute(irPlan);
    fftw_destroy_plan(irPlan);
    fftw_free(in);

    return out;
}

vector<double> convolved_block(vector<double> in_vector, vector<complex<double> > ir_cpx_vector, int size) {
    fftw_complex* outputFFT = fftw_complex_transform(in_vector);

    vector<vector<double> > products;
    vector<complex<double> > sig_fft_cpx;
    for (int i=0; i<size; i++) {
        complex<double> m1 (outputFFT[i][0], outputFFT[i][1]);
        sig_fft_cpx.push_back(m1);
    }        
    fftw_free(outputFFT);
    for (int j=0; j<size; j++) {
        std::complex<double> complexProduct = sig_fft_cpx[j]*ir_cpx_vector[j];
        double re = real(complexProduct);
        double im = imag(complexProduct);
        vector<double> elemVec(2);
        elemVec[0] = re;
        elemVec[1] = im;

        products.push_back(elemVec);
    }
    //fftw_complex* revFFT = ifft_from_complex_vector(products);
    double* revFFT = ifft_from_complex_vector(products);
    vector<double> out_vec_dbl;

    for (int i=0; i<size; i++) {
        //out_vec_dbl.push_back(outputFFT[i][0]);
        //out_vec_dbl.push_back(revFFT[i][0]);
        out_vec_dbl.push_back(revFFT[i]);
        //out_vec_dbl.push_back(outputFFT[i]);
    }
    //fftw_free(revFFT);
    free(revFFT);
    return out_vec_dbl;
}

vector<double> padded(vector<double> input, int total) {
    vector<double> padded_vec;
    for (int i = 0; i<input.size(); i++) {
    padded_vec.push_back(input[i]);
    }
    int numZeroes = total - input.size();
    for (int i = 0; i< numZeroes; i++) {
    padded_vec.push_back((double)0);
    }
    return padded_vec;
}

void testDummyData() {
    vector<double> dummyFilter;
    dummyFilter.push_back(1);
    dummyFilter.push_back(-1);
    dummyFilter.push_back(1);

    vector<double> dummySignal;
    dummySignal.push_back(3);
    dummySignal.push_back(-1);
    dummySignal.push_back(0);
    dummySignal.push_back(3);
    dummySignal.push_back(2);
    dummySignal.push_back(0);
    dummySignal.push_back(1);
    dummySignal.push_back(2);
    dummySignal.push_back(1);

    const int nearWindowSize=3;
    const int nearIrLength=3;
    const int totalLength = 5;

    dummyFilter = padded(dummyFilter, totalLength);
    vector<complex<double> > dummy_ir_vec = filter_kernel_cpx_vec(dummyFilter);

    int localNumSections = 3;
    vector<vector<double> > outputs;
    for (int j=0; j<localNumSections; j++) {
        vector<double> local_section;
        for (int i; i<nearWindowSize; i++) {
            int idx = j*nearWindowSize + i;
            local_section.push_back(dummySignal[idx]);
        }
        local_section = padded(local_section, totalLength);
        vector<double> local_output = convolved_block(local_section, dummy_ir_vec, totalLength);
        outputs.push_back(local_output);
    }
    vector<double> local_results(11,0); // example has 11 in output

    for (int j=0; j<localNumSections; j++) {
        vector<double> local_totals_arr = outputs[j];
        cout << "overlap summing section " << j << "\n";
        for (int i=0; i<local_totals_arr.size(); i++) {
            int newIdx = j*nearWindowSize+i;
            local_results[newIdx] += local_totals_arr[i];
        }
    }    
    for (int i=0; i<11; i++) {
        cout << "result " << i << "\n";
        cout << local_results[i] << "\n";
    }
}
...