Właśnie poprawiłem mój algorytm obliczania wysokości tonu przy użyciu algorytmu widma produktu harmonicznego. Byłem po prostu ciekawy, dlaczego to wyjaśnienie spektrum produktów harmonicznych mówi, że musisz zaimplementować okno Hanninga do zbioru danych. Jaki byłby skutek zaimplementowania innych funkcji okna na zestawie danych (a następnie FFT)? Która funkcja okienkowa jest rzeczywiście najlepsza do wykrywania częstotliwości? Oto metody, których użyłem w kodzie:
/** * 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); }
Odpowiedź
FFT można przeprowadzić tylko na ograniczonej porcji danych. Podstawowa matematyka opiera się na założeniu, że sygnał w dziedzinie czasu jest okresowy, tj. Twoja porcja danych jest powtarzana w czasie. Zwykle powoduje to dużą nieciągłość na krawędziach bryły. Spójrzmy na szybki przykład: rozmiar FFT = 1000 punktów, częstotliwość próbkowania = 1000 Hz, rozdzielczość częstotliwości = 1 Hz. Jeśli masz falę sinusoidalną 10 Hz, nie masz nieciągłości, ponieważ dokładnie 10 okresów mieści się w oknie FFT a wartości (i pochodne) na krawędziach są takie same. FFT tego siganla będzie wynosić zero z wyjątkiem pojedynczej wartości w przedziale nr 10. Działa to również dla fali sinusoidalnej 11 Hz. Jednak dla fali sinusoidalnej 10,3 Hz kończy się z dużą nieciągłością, a FFT będzie miał energię we wszystkich pojemnikach z maksymalnie około 10 lub 11, a następnie „spódnice”, które toczą się na boki. Tak więc niewielka zmiana częstotliwości powoduje ogromną zmianę w obrazie FFT.
Aby tego uniknąć, stosuje się okienkowanie: Windows upewnia się, że dane na krawędziach są równe zeru, więc nie ma nieciągłości. Jednak zwielokrotnienie w dziedzinie czasu jest splotem w dziedzinie częstotliwości, co powoduje poszerzenie linii widmowych, a także płatów bocznych. Wybór okna kontroluje kompromisy między szerokością płata głównego a odstępem i wysokością płatów bocznych. Wymagania specyficzne dla aplikacji określają, którego okna użyć, i są dziesiątki opcji. Hanning jest tylko jednym z nich. Zasadniczo jest to okno z wyboru, jeśli nie masz żadnych lepszych pomysłów. Osobiście wolę okna Kaisera, ponieważ mają one ciągły parametr, który może kontrolować zachowanie okna w szerokim zakresie.
Ogólnie FFT nie jest świetną metodą wykrywania wysokości tonu. W przypadku większości sygnałów audio maksimum w widmie NIE jest podstawą (zwykle harmoniczne mają wyższą energię), aby uzyskać przyzwoitą rozdzielczość, potrzebujesz długich fragmentów danych, ale to sprawia, że algorytm bardzo powolny i powolny reaguje na zmiany. Znacznie lepszymi opcjami są pętle wyglądające na fazę, pętle wyglądające na opóźnienia, autokorelacje, tracker max / min, tracker zero cross, itp.