我正在构建一个需要能够显示实时光谱分析仪的应用程序。这是我能够在iOS上成功制作的版本:
我正在使用Wendykierp JTransforms库执行FFT计算,并设法捕获音频数据并执行FFT功能。见下文:
short sData[] = new short[BufferElements2Rec];
int result = audioRecord.read(sData, 0, BufferElements2Rec);
try
{
//Initiate FFT
DoubleFFT_1D fft = new DoubleFFT_1D(sData.length);
//Convert sample data from short[] to double[]
double[] fftSamples = new double[sData.length];
for (int i = 0; i < sData.length; i++) {
//IMPORTANT: We cannot simply cast the short value to double.
//As a double is only 2 bytes (values -32768 to 32768)
//We must divide by 32768 before we cast to Double.
fftSamples[i] = (double) sData[i] / 32768;
}
//Perform fft calcs
fft.realForward(fftSamples);
//TODO - Convert FFT data into 20 "bands"
} Catch (Exception e)
{
}
在iOS中,我使用的是一个库(Tempi-FFT),该库具有用于计算幅度,频率以及为任何给定数量的频段提供平均数据的内置功能(如您所见,我正在使用20个频段上图)。似乎我没有这个图书馆那么豪华,我需要自己计算。
查找有关如何对FFT计算返回的数据进行互操作的任何好的示例或教程。这是我正在接收的一些示例数据:
-11387.0, 183.0, -384.9121475854448, -224.66315714636642, -638.0173005872095, -236.2318653974911, -1137.1498541119106, -437.71599514435786, 1954.683405957685, -2142.742125980924 ...
寻找有关如何解释此数据的简单说明。我看过的其他一些问题,或者我听不懂,或者没有提供有关如何确定给定频段数量的信息:
答案 0 :(得分:1)
您的问题可以分为两部分:确定所有频率的幅度(解释输出)和将频率平均划分为频段
我不会介绍快速傅立叶变换/离散傅立叶变换的复杂性(如果您想了解基本知识,请参阅this video),但是知道其中有一个真实的和虚构的部分每个输出。
realForward
函数的文档描述了虚部和实部在输出数组中的位置(我假设您的样本量均匀):
a[2*k] = Re[k], 0 <= k < n / 2 a[2*k+1] = Im[k], 0 < k < n / 2 a[1] = Re[n/2]
a
等同于您的fftSamples
,这意味着我们可以按以下方式将该文档翻译成代码(我将Re
和Im
更改为{{1} }和realPart
):
imaginaryPart
现在,我们有了每个频率的实部和虚部。我们可以使用实部作为x值并将虚部作为y值在x-y坐标平面上绘制它们。这将创建一个三角形,并且三角形的斜边的长度就是频率的大小。我们可以使用毕达哥拉斯定理得到这样的大小:
int n = fftSamples.length;
double[] realPart = new double[n / 2];
double[] imaginaryPart = new double[n / 2];
for(int k = 0; k < n / 2; k++) {
realPart[k] = fftSamples[k * 2];
imaginaryPart[k] = fftSamples[k * 2 + 1];
}
realPart[n / 2] = fftSamples[1];
请注意,频谱的第0个索引没有虚部。这是信号的DC component(我们将不使用它)。
现在,我们有了一个数组,其中包含您频谱中每个频率的幅度(如果采样频率为44100Hz,这意味着您现在有了一个数组,其幅度介于0Hz和44100Hz之间,如果您有441个值在您的数组中,则每个索引值代表一个100Hz的步进。)
现在,我们已经将FFT输出转换为可以使用的数据,我们可以继续进行问题的第二部分:查找不同频段的平均值。这是相对简单的。我们只需要将数组拆分为不同的频段,然后找到每个频段的平均值即可。可以这样概括:
double[] spectrum = new double[n / 2];
for(int k = 1; k < n / 2; k++) {
spectrum[k] = Math.sqrt(Math.pow(realPart[k], 2) + Math.pow(imaginaryPart[k], 2));
}
spectrum[0] = realPart[0];
就是这样!现在,您有了一个名为int NUM_BANDS = 20; //This can be any positive integer.
double[] bands = new double[NUM_BANDS];
int samplesPerBand = (n / 2) / NUM_BANDS;
for(int i = 0; i < NUM_BANDS; i++) {
//Add up each part
double total;
for(int j = samplesPerBand * i ; j < samplesPerBand * (i+1); j++) {
total += spectrum[j];
}
//Take average
bands[i] = total / samplesPerBand;
}
的数组,其中包含每个频段的平均幅度。上面的代码有意未经过优化,以显示每个步骤的工作方式。这是经过简化和优化的版本:
bands
这是一个很长的答案,而且我尚未测试代码(尽管我确实打算这样做)。如果发现任何错误,请随时发表评论。