JTransforms FFT en Android de datos de PCM
He estado jugando con esto ahora por algún tiempo, no puedo resolver lo que estoy destinado a hacer aquí.
Estoy leyendo en datos de audio PCM en una matriz audioData:
- Conversión de archivos MIDI a audio sin procesar utilizando un sintetizador de software
- ¿Valores de amplitud de audio PCM?
- Dificultad para portar el código de salida de PCM sin procesar de Java a Android AudioTrack API
- Escritura de datos grabados PCM en un archivo .wav (java android)
- Mensaje de notificación de AudioTrack corriente
recorder.read(audioData,0,bufferSize); //read the PCM audio data into the audioData array
Quiero usar la biblioteca JTransform de Piotr Wendykier para preformar una FFT en mis datos PCM para obtener la frecuencia.
import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;
Por el momento tengo esto:
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);
No puedo hacer sentido de cómo trabajar esto, alguien puede darme algunos consejos? ¿Tendré que realizar cálculos después de esto?
Estoy seguro de que estoy muy lejos, cualquier cosa sería muy apreciada!
Ben
- ¿Puedo obtener datos crudos de cpm de MediaPlayer o SoundPool?
- Emitir codificación y decodificación de una grabación de audio al formato G711 (PCMU - uLaw)
- Manera correcta de convertir datos de onda PCM de 16 bits a flotar
- Cómo convertir archivos .pcm a .wav o .mp3?
- AudioTrack - matriz corta a la distorsión de la matriz del byte usando jlayer (decodificador del java mp3)
- Descodificación de archivos de audio y re-codificación al formato PCM deseado: 44,100 kHz, 2 canales, 16 bits
- PCM de Android a Ulaw que codifica el archivo wav
- ¿Cómo se puede conectar un efecto de reverberación a los datos de AudioRecord / PCM y guardarlo en un archivo?
Si sólo está buscando la frecuencia de un solo tono sinusoidal en la forma de onda de entrada, entonces necesita encontrar el pico FFT con la magnitud más grande, donde:
Magnitude = sqrt(re*re + im*im)
El índice i
de este pico de magnitud más grande le indicará la frecuencia aproximada de su sinusoide:
Frequency = Fs * i / N
dónde:
Fs = sample rate (Hz) i = index of peak N = number of points in FFT (1024 in this case)
Sí, usted necesita utilizar la función realForward en lugar de complexForward, porque le pasa una matriz real y no una matriz compleja de doc .
EDITAR:
O puede obtener la parte real y realizar complejo a fft complejo como este:
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);
Procuro y comparo resultados con matlab, y no obtengo los mismos resultados … (magnitud)
Ya que he pasado algunas horas en conseguir esto para trabajar aquí es una implementación completa en 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
devolverá una aproximación de la frecuencia dominante en los datos de muestra presentados. Aplica una ventana Hamming a su entrada para permitir el paso de muestras arbitrarias de un flujo de audio. La precisión se consigue mediante relleno interno cero de los datos de la muestra antes de realizar la transformación FFT. (Sé que hay maneras mejores – y mucho más complejas de hacer esto pero el acercamiento del relleno es suficiente para mis necesidades personales).
I testet it contra raw 16bit PCM muestras creadas a partir de sonidos de referencia para 220hz y 440hz y los resultados coinciden.
Si está buscando la FFT de una entrada de audio (1D, datos reales), ¿no debería estar usando el 1D REAL FFT?