Ваш вопрос можно разделить на две части: нахождение величины всех частот (интерпретация выходных данных) и усреднение частот по полосам
Нахождение величины всех частот:
Я не буду вдаваться в тонкости быстрого преобразования Фурье / дискретного преобразования Фурье (если вы хотите получить базовое понимание, см. это видео ), но знайте, что в каждом выводе есть действительная и мнимая части.
В документации к функции 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
на realPart
и imaginaryPart
соответственно):
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];
Теперь у нас есть действительная и мнимая части каждой частоты.Мы могли бы построить их на координатной плоскости xy, используя действительную часть в качестве значения x и мнимую часть в качестве значения y.Это создает треугольник, и длина гипотенузы треугольника является величиной частоты.Мы можем использовать теорему Пифагора, чтобы получить эту величину:
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];
Обратите внимание, что 0-й индекс спектра не имеет мнимой части.Это постоянная составляющая сигнала (мы не будем его использовать).
Теперь у нас есть массив с величинами каждой частоты в вашем спектре (если ваша частота дискретизации равна44100 Гц, это означает, что теперь у вас есть массив с величинами частот между 0 Гц и 44100 Гц, и если у вас есть 441 значение в вашем массиве, то каждое значение индекса представляет шаг 100 Гц.)
Усреднение частот по полосам:
Теперь, когда мы преобразовали вывод БПФ в данные, которые мы можем использовать, мы можем перейти ко второй части вашего вопроса: нахождение средних значенийразные полосы частот.Это относительно просто.Нам просто нужно разбить массив на разные полосы и найти среднее значение для каждой полосы.Это можно обобщить так:
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
со средней величиной каждой полосы частот.Приведенный выше код специально не оптимизирован, чтобы показать, как работает каждый шаг.Вот сокращенная и оптимизированная версия:
int numFrequencies = fftSamples.length / 2;
double[] spectrum = new double[numFrequencies];
for(int k = 1; k < numFrequencies; k++) {
spectrum[k] = Math.sqrt(Math.pow(fftSamples[k*2], 2) + Math.pow(fftSamples[k*2+1], 2));
}
spectrum[0] = fftSamples[0];
int NUM_BANDS = 20; //This can be any positive integer.
double[] bands = new double[NUM_BANDS];
int samplesPerBand = numFrequencies / 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;
}
//Use bands in view!
Это был очень длинный ответ, и я еще не тестировал код (хотя планирую).Не стесняйтесь комментировать, если вы обнаружите какие-либо ошибки.