AudioRecorder |Интерпретация данных FFT для Spectrum Analyzer - PullRequest
0 голосов
/ 24 сентября 2018

Я создаю приложение, которое должно отображать спектральный анализатор в реальном времени.Вот версия, которую я смог успешно сделать на iOS:

Sample Spectral Analyser

Я использую библиотеку 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 ...

Ищете простое объяснение того, как интерпретировать эти данные.Некоторые другие вопросы, на которые я смотрел, которые я не мог понять или не предоставил информацию о том, как определить данное число полос:

Спектральная плотность мощности от jTransforms DoubleFFT_1D

Как разработать анализатор спектра из звука в реальном времени?

1 Ответ

0 голосов
/ 22 ноября 2018

Ваш вопрос можно разделить на две части: нахождение величины всех частот (интерпретация выходных данных) и усреднение частот по полосам

Нахождение величины всех частот:

Я не буду вдаваться в тонкости быстрого преобразования Фурье / дискретного преобразования Фурье (если вы хотите получить базовое понимание, см. это видео ), но знайте, что в каждом выводе есть действительная и мнимая части.

В документации к функции 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!

Это был очень длинный ответ, и я еще не тестировал код (хотя планирую).Не стесняйтесь комментировать, если вы обнаружите какие-либо ошибки.

...