Así que acabo de revisar mi algoritmo de cálculo de tono utilizando un algoritmo de espectro de producto armónico. Solo tenía curiosidad por saber por qué esta explicación de Harmonic Product Spectrum establece que es necesario implementar una ventana Hanning en el conjunto de datos. ¿Cuál sería el efecto de implementar otras funciones de Windows en un conjunto de datos (y luego aplicar FFT)? ¿Qué función de ventana es la mejor para la detección de frecuencia? Estos son los métodos relevantes que he usado en mi código:
/** * Calculates the Frequency based off of the byte array, * @param bytes The audioData you want to analyze * @return The calculated frequency in Hertz. */ private int getFrequency(byte[] bytes){ double[] audioData = this.bytesToDoubleArray(bytes); audioData = applyHanningWindow(audioData); Complex[] complex = new Complex[audioData.length]; for(int i = 0; i<complex.length; i++){ complex[i] = new Complex(audioData[i], 0); } Complex[] fftTransformed = FFT.fft(complex); //return calculateFrequency(fftTransformed); System.out.println("Max size:" + (fftTransformed.length*getFFTBinSize(fftTransformed.length)/4)); return calculateFundamentalFrequency(fftTransformed,4); } private double[] applyHanningWindow(double[] data){ return applyHanningWindow(data, 0, data.length); } private double[] applyHanningWindow(double[] signal_in, int pos, int size) { for (int i = pos; i < pos + size; i++) { int j = i - pos; // j = index into Hann window function signal_in[i] = (double)(signal_in[i] * 0.5 * (1.0 - Math.cos(2.0 * Math.PI * j / size))); } return signal_in; } /** * Harmonic Product Spectrum * @param fftData * @param n * @return */ private int calculateFundamentalFrequency(Complex[] fftData, int n){ Complex[][] data = new Complex[n][fftData.length/n]; for(int i = 0; i<n; i++){ for(int j = 0; j<data[0].length; j++){ data[i][j] = fftData[j*(i+1)]; } } Complex[] result = new Complex[fftData.length/n];//Combines the arrays for(int i = 0; i<result.length; i++){ Complex tmp = new Complex(1,0); for(int j = 0; j<n; j++){ tmp = tmp.times(data[j][i]); } result[i] = tmp; } //Calculates Maximum Magnitude of the array double max = Double.MIN_VALUE; int index = -1; for(int i = 0; i<result.length; i++){ Complex c = result[i]; double tmp = c.getMagnitude(); if(tmp>max){ max = tmp;; index = i; } } return index*getFFTBinSize(fftData.length); }
Responder
La FFT solo se puede realizar sobre una cantidad limitada de datos. La matemática básica se basa en la suposición de que la señal en el dominio del tiempo es periódica, es decir, su fragmento de datos se repite en el tiempo. Eso normalmente da como resultado una discontinuidad importante en los bordes del trozo. Veamos un ejemplo rápido: tamaño de FFT = 1000 puntos, frecuencia de muestreo = 1000 Hz, resolución de frecuencia = 1 Hz. Si tiene una onda sinusoidal de 10 Hz, no tiene una discontinuidad, ya que exactamente 10 períodos encajan en su ventana de FFT y los valores (y derivados) en los bordes son los mismos. La FFT de este siganl será cero excepto por un solo valor en el bin # 10. Esto también funciona para una onda sinusoidal de 11 Hz. Sin embargo, para la onda sinusoidal de 10,3 HZ, terminas con mucha discontinuidad y la FFT tendrá energía en todos los contenedores con un máximo de alrededor de 10 u 11 y luego «faldas» que ruedan hacia los lados. Entonces, un pequeño cambio en la frecuencia da como resultado un cambio masivo en la imagen FFT.
Las ventanas se utilizan para evitar esto: Windows se asegura de que los datos en los bordes sean cero, para que no haya discontinuidad. Sin embargo, la multiplicación en el dominio del tiempo es una convolución en el dominio de la frecuencia y eso da como resultado un ensanchamiento de las líneas espectrales y también de los lóbulos laterales. La elección de la ventana controla las compensaciones entre el ancho del lóbulo principal y el espacio y la altura de los lóbulos laterales. Los requisitos específicos de su aplicación determinan qué ventana usar y hay docenas de opciones. Hanning es solo uno de ellos. Es «básicamente» la ventana de elección si no «tiene mejores ideas». Personalmente, prefiero las ventanas de Kaiser porque tienen un parámetro continuo que puede controlar el comportamiento de la ventana en un amplio rango.
En general, FFT no es un gran método para la detección de tono. Para la mayoría de las señales de audio, el máximo en el espectro NO es el fundamental (típicamente los armónicos tienen mayor energía), para obtener una resolución decente necesita una gran cantidad de datos, pero eso hace que el algoritmo sea muy lento y lento para responder a los cambios. Las opciones mucho mejores son bucles de mirada de fase, bucles de mirada de retardo, autocorrelaciones, rastreador máximo / mínimo, rastreador de cruce por cero, etc.