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

问题描述 投票:1回答:1

我正在尝试使用以下代码的输出制作彩色波形。但是当我运行它时,我只获得某些数字(参见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

它只显示某些数字,而不是它可能应该的各种频率。还是我错了?你们可以看到我的代码有什么问题吗?颜色的东西被注释掉了,因为我还没有这样做。

ios c swift signal-processing fft
1个回答
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来增加用于分析的样本数量(如评论“用于分析的帧大小,您可能想要使用此”)所建议的。

© www.soinside.com 2019 - 2024. All rights reserved.