使用 FFT 进行频谱分析,基频推导

Posted

技术标签:

【中文标题】使用 FFT 进行频谱分析,基频推导【英文标题】:Spectral analysis using FFT, fundamental frequency derivation 【发布时间】:2012-05-14 12:11:59 【问题描述】:

我需要对一个简单的 wav 文件进行频谱分析。 我已经做过的事情:

将文件读入字节数组:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead = 0;

while ((bytesRead = audiostream.read(buffer)) != -1) 
    baos.write(buffer, 0, bytesRead);


fileByteArray = baos.toByteArray();

然后我将其转换为实际值(双精度值)。 我已经将样本值存储在 double[] 数组中。

如何对这些样本进行 FFT + 估计基频?

使用JTranforms 库我尝试过这样的事情:

DoubleFFT_1D fft = new DoubleFFT_1D(reader.getSpectrum().getYvalues().length);
double[] x = reader.getSpectrum().getYvalues();
double[] frequencyArray = new double[x.lenght/2];
double[] amplitudeArray = new double[x.lenght/2];
fft.realForward(x);
int i=0;
for (int j = 0; j < x.length-2; j += 2) 
    i++;
    this.frequencyArray[i] = i;
    this.amplitudeArray[i] = Math.sqrt(Math.pow(doub[j],2) + Math.pow(doub[j + 1],2));

对吗?

感谢所有建议;)

【问题讨论】:

一个问题是您似乎没有在 FFT 之前应用合适的窗口函数。 自相关函数是获取基频的另一种方法。 【参考方案1】:

您应该使用可以通过 FFT 高效计算的 自相关

DoubleFFT_1D fft = new DoubleFFT_1D(reader.getSpectrum().getYvalues().length);
DoubleFFT_1D ifft = new DoubleFFT_1D(reader.getSpectrum().getYvalues().length);

fft.realForward(x);
for (int i = 0; i < x.length/2; i++) 
    x[2*i] = Math.sqrt(Math.pow(x[2*i],2) + Math.pow(x[2*i+1],2));
    x[2*i+1] = 0;

ifft.realInverse(x);
for (int i = 1; i < x.length; i++)
    x[i] /= x[0];
x[0] = 1.0;

此代码为您提供了一个值列表:

x[i]: 与 i 班次相关

因此,例如,如果 x[n] 的值较高(接近 1),则意味着您有一个基本信号周期:n*(1000/sampleRateHz) 毫秒。这相当于频率:sampleRateHz/(1000*n)

【讨论】:

【参考方案2】:

频率数组中的值需要与采样率和FFT的长度相关。

您仍然需要解决确定基频与峰值频率的问题。为此,您可能需要使用音高检测/估计算法,其中有很多(查找有关该主题的研究论文)。

【讨论】:

以上是关于使用 FFT 进行频谱分析,基频推导的主要内容,如果未能解决你的问题,请参考以下文章

关于用FFT分析信号频谱的问题

在 Java 中使用 FFT 算法进行音频频谱分析

使用 FFT 进行频谱分析

如何用 FFT 进行频谱分析? [关闭]

用MATLAB设计对信号进行频谱分析和滤波处理的程序

p68 对数变换 关于fft2 fftshift 频谱