JTransformation FFT в Android из данных PCM - PullRequest
4 голосов
/ 04 октября 2011

Я уже некоторое время играю с этим, я не могу понять, что я собираюсь делать здесь.

Я читаю аудиоданные PCM в массив audioData:

 recorder.read(audioData,0,bufferSize);     //read the PCM audio data into the audioData array

Я хочу использовать библиотеку JTransform Петра Вендикера, чтобы предварительно преобразовать БПФ в мои данные PCM, чтобы получить частоту.

import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;

На данный момент у меня есть это:

       DoubleFFT_1D fft = new DoubleFFT_1D(1024); // 1024 is size of array

for (int i = 0; i < 1023; i++) {
           a[i]= audioData[i];               
           if (audioData[i] != 0)
           Log.v(TAG, "audiodata=" + audioData[i] + " fft= " + a[i]);
       }
       fft.complexForward(a);

Я не могу понять, как это сделать, может кто-нибудь дать мне несколько советов?Должен ли я выполнить какие-либо вычисления после этого?

Я уверен, что я далеко, все будет с благодарностью!

Бен

Ответы [ 4 ]

9 голосов
/ 04 октября 2011

Если вы просто ищете частоту одиночного синусоидального тона во входном сигнале, то вам нужно найти пик FFT с наибольшей величиной, где:

Magnitude = sqrt(re*re + im*im)

Индекс iэта самая большая величина пика скажет вам приблизительную частоту вашей синусоиды:

Frequency = Fs * i / N

где:

Fs = sample rate (Hz)
i = index of peak
N = number of points in FFT (1024 in this case)
3 голосов
/ 09 марта 2016

Поскольку я потратил несколько часов на то, чтобы заставить это работать, вот полная реализация в Java:

import org.jtransforms.fft.DoubleFFT_1D;

public class FrequencyScanner {
    private double[] window;

    public FrequencyScanner() {
        window = null;
    }

    /** extract the dominant frequency from 16bit PCM data.
     * @param sampleData an array containing the raw 16bit PCM data.
     * @param sampleRate the sample rate (in HZ) of sampleData
     * @return an approximation of the dominant frequency in sampleData
     */
    public double extractFrequency(short[] sampleData, int sampleRate) {
        /* sampleData + zero padding */
        DoubleFFT_1D fft = new DoubleFFT_1D(sampleData.length + 24 * sampleData.length);
        double[] a = new double[(sampleData.length + 24 * sampleData.length) * 2];

        System.arraycopy(applyWindow(sampleData), 0, a, 0, sampleData.length);
        fft.realForward(a);

        /* find the peak magnitude and it's index */
        double maxMag = Double.NEGATIVE_INFINITY;
        int maxInd = -1;

        for(int i = 0; i < a.length / 2; ++i) {
            double re  = a[2*i];
            double im  = a[2*i+1];
            double mag = Math.sqrt(re * re + im * im);

            if(mag > maxMag) {
                maxMag = mag;
                maxInd = i;
            }
        }

        /* calculate the frequency */
        return (double)sampleRate * maxInd / (a.length / 2);
    }

    /** build a Hamming window filter for samples of a given size
     * See http://www.labbookpages.co.uk/audio/firWindowing.html#windows
     * @param size the sample size for which the filter will be created
     */
    private void buildHammWindow(int size) {
        if(window != null && window.length == size) {
            return;
        }
        window = new double[size];
        for(int i = 0; i < size; ++i) {
            window[i] = .54 - .46 * Math.cos(2 * Math.PI * i / (size - 1.0));
        }
    }

    /** apply a Hamming window filter to raw input data
     * @param input an array containing unfiltered input data
     * @return a double array containing the filtered data
     */
    private double[] applyWindow(short[] input) {
        double[] res = new double[input.length];

        buildHammWindow(input.length);
        for(int i = 0; i < input.length; ++i) {
            res[i] = (double)input[i] * window[i];
        }
        return res;
    }
}

FrequencyScanner вернет приближение доминирующей частоты в представленных данных выборки.Он применяет окно Хэмминга к своему входу, чтобы разрешить передачу произвольных сэмплов из аудиопотока.Точность достигается за счет внутреннего заполнения нуля данных образца перед выполнением преобразования FFT.(Я знаю, что есть лучшие - и гораздо более сложные - способы сделать это, но подход с заполнением достаточен для моих личных потребностей.)и результаты совпадают.

2 голосов
/ 24 июня 2012

Да, вам нужно использовать функцию realForward вместо complexForward, потому что вы передаете ему реальный массив, а не сложный массив из документа .

РЕДАКТИРОВАТЬ:

Иливы можете получить реальную часть и выполнить сложное по сложному fft следующим образом:

double[] in = new double[N];
read ...
double[] fft = new double[N * 2];

for(int i = 0; i < ffsize; ++i)
{
  fft[2*i] = mic[i];
  fft[2*i+1] = 0.0;
}
fft1d.complexForward(fft);

Я пытаюсь сравнить результаты с matlab, и я не получаю такие же результаты ... (величина)

1 голос
/ 26 октября 2011

Если вы ищете БПФ аудио входа (1D, реальные данные), разве вы не должны использовать 1D РЕАЛЬНОЕ БПФ?

...