Как получить более точный вывод из БПФ? - PullRequest
1 голос
/ 22 марта 2019

Я пытаюсь создать цветную форму волны, используя вывод следующего кода. Но когда я запускаю его, я получаю только определенные числа (см. Переменную freq, она использует размер бина, частоту кадров и индекс для создания этих частот) в качестве выходных частот. Я не эксперт по математике, хотя я собрал это вместе из существующего кода и ответов.

//
//  colored_waveform.c
//  MixDJ
//
//  Created by Jonathan Silverman on 3/14/19.
//  Copyright © 2019 Jonathan Silverman. All rights reserved.
//

#include "colored_waveform.h"
#include "fftw3.h"
#include <math.h>
#include "sndfile.h"

//int N = 1024;

// helper function to apply a windowing function to a frame of samples
void calcWindow(double* in, double* out, int size) {
    for (int i = 0; i < size; i++) {
        double multiplier = 0.5 * (1 - cos(2*M_PI*i/(size - 1)));
        out[i] = multiplier * in[i];
    }
}

// helper function to compute FFT
void fft(double* samples, fftw_complex* out, int size) {

    fftw_plan p;
    p = fftw_plan_dft_r2c_1d(size, samples, out, FFTW_ESTIMATE);

    fftw_execute(p);
    fftw_destroy_plan(p);

}

// find the index of array element with the highest absolute value
// probably want to take some kind of moving average of buf[i]^2
// and return the maximum found
double maxFreqIndex(fftw_complex* buf, int size, float fS) {
    double max_freq = 0;
    double last_magnitude = 0;
    for(int i = 0; i < (size / 2) - 1; i++) {
        double freq = i * fS / size;
//        printf("freq: %f\n", freq);
        double magnitude = sqrt(buf[i][0]*buf[i][0] + buf[i][1]*buf[i][1]);
        if(magnitude > last_magnitude)
            max_freq = freq;
        last_magnitude = magnitude;

    }
    return max_freq;
}
//
//// map a frequency to a color, red = lower freq -> violet = high freq
//int freqToColor(int i) {
//
//}

void generateWaveformColors(const char path[]) {
    printf("Generating waveform colors\n");
    SNDFILE     *infile = NULL;
    SF_INFO     sfinfo;

    infile = sf_open(path, SFM_READ, &sfinfo);


    sf_count_t numSamples = sfinfo.frames;

    // sample rate
    float fS = 44100;

//    float songLengLengthSeconds = numSamples / fS;

//    printf("seconds: %f", songLengLengthSeconds);

    // size of frame for analysis, you may want to play with this
    float frameMsec = 5;

    // samples in a frame
    int frameSamples = (int)(fS / (frameMsec * 1000));

    // how much overlap each frame, you may want to play with this one too
    int frameOverlap = (frameSamples / 2);

    // color to use for each frame
//    int outColors[(numSamples / frameOverlap) + 1];

    // scratch buffers
    double* tmpWindow;
    fftw_complex* tmpFFT;

    tmpWindow = (double*) fftw_malloc(sizeof(double) * frameSamples);
    tmpFFT = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * frameSamples);

    printf("Processing waveform for colors\n");
    for (int i = 0, outptr = 0; i < numSamples; i += frameOverlap, outptr++)
    {
        double inSamples[frameSamples];

        sf_read_double(infile, inSamples, frameSamples);

        // window another frame for FFT
        calcWindow(inSamples, tmpWindow, frameSamples);

        // compute the FFT on the next frame
        fft(tmpWindow, tmpFFT, frameSamples);

        // which frequency is the highest?
        double freqIndex = maxFreqIndex(tmpFFT, frameSamples, fS);

        printf("%i: ", i);
        printf("Max freq: %f\n", freqIndex);
        // map to color
//        outColors[outptr] = freqToColor(freqIndex);
    }
    printf("Done.");
    sf_close (infile);

}

Вот некоторые из выводов:

2094216: Max freq: 5512.500000
2094220: Max freq: 0.000000
2094224: Max freq: 0.000000
2094228: Max freq: 0.000000
2094232: Max freq: 5512.500000
2094236: Max freq: 5512.500000

Показывает только определенные цифры, а не широкий спектр частот, как это может быть. Или я не прав? Ребята, что-то не так с моим кодом? Цветовые комментарии закомментированы, потому что я еще этого не сделал.

1 Ответ

1 голос
/ 23 марта 2019

Частотное разрешение БПФ ограничено длиной имеющейся у вас выборки данных. Чем больше сэмплов, тем выше разрешение по частоте.

В вашем конкретном случае вы выбрали кадры по 5 миллисекунд, которые затем преобразуются в число выборок в следующей строке:

// samples in a frame
int frameSamples = (int)(fS / (frameMsec * 1000));

Это соответствует только 8 выборкам с указанной частотой дискретизации 44100 Гц. Разрешение по частоте с таким небольшим размером кадра может быть вычислено как

44100 / 8

или 5512,5 Гц, довольно плохое разрешение. Соответственно, наблюдаемые частоты всегда будут равны 0, 5512,5, 11025, 16537,5 или 22050 Гц.

Чтобы получить более высокое разрешение, вы должны увеличить количество выборок, используемых для анализа, увеличив frameMsec (как подсказывает комментарий «размер кадра для анализа, вы можете поиграть с этим»).

...