JTransformation FFT в Android из данных PCM
Я играл с этим сейчас некоторое время, я не могу понять, что я должен делать здесь.
Я читаю в PCM аудио данные в массив audioData:
recorder.read(audioData,0,bufferSize); //read the PCM audio data into the audioData array
Я хочу использовать библиотеку Петра Вендикира JTransform, чтобы предварительно преобразовать FFT в мои данные 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 ответа
Если вы просто ищете частоту одиночного синусоидального тона во входном сигнале, то вам нужно найти пик 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)
Поскольку я потратил несколько часов на то, чтобы заставить это работать, вот полная реализация на 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.
(Я знаю, что есть лучшие - и гораздо более сложные - способы сделать это, но подход подбора достаточен для моих личных потребностей).
Я проверяю его на необработанных 16-битных сэмплах PCM, созданных из эталонных звуков для 220 Гц и 440 Гц, и результаты совпадают.
Да, вам нужно использовать функцию realForward вместо complexForward, потому что вы передаете ей реальный массив, а не сложный массив из doc.
РЕДАКТИРОВАТЬ:
Или вы можете получить реальную часть и выполнить сложное к сложному 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, и я не получаю те же результаты... (величина)
Если вы ищете БПФ аудиовхода ( 1D, реальные данные), разве вы не должны использовать 1D РЕАЛЬНОЕ БПФ?