如何从FFT中获得更精确的输出?

时间:2019-03-22 02:13:41

标签: ios c swift signal-processing fft

我正在尝试使用以下代码的输出制作彩色波形。但是,当我运行它时,我只会得到某些数字(请参阅freq变量,它使用bin大小,帧速率和索引来设定这些频率)作为输出频率。我不是数学专家,即使我从现有的代码和答案中总结了这一点。

//
//  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 个答案:

答案 0 :(得分:1)

FFT的频率分辨率受您拥有的数据样本长度的限制。您拥有的样本越多,频率分辨率越高。

在特定情况下,您选择了5毫秒的帧,然后将其转换为以下行上的许多样本:

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

在指定的44100Hz采样率下,这仅对应8个采样。如此小的帧尺寸的频率分辨率可以计算为

44100 / 8

或5512.5Hz,分辨率很差。相应地,观测到的频率将始终是0、5512.5、11025、16537.5或22050Hz之一。

要获得更高的分辨率,您应该通过增加frameMsec来增加用于分析的样本数量(如注释“用于分析的框架的大小,您可能需要使用它”)。