Právě jsem tedy revidoval svůj algoritmus výpočtu výšky tónu pomocí algoritmu harmonického spektra produktu. Byl jsem jen zvědavý, proč toto vysvětlení Harmonického spektra produktů uvádí, že musíte do datové sady implementovat Hanningovo okno. Jaký by byl účinek implementace dalších funkcí Window na datovou sadu (a její následné FFTing)? Která funkce Windowing je ve skutečnosti nejlepší pro detekci frekvence? Zde jsou relevantní metody, které jsem použil ve svém kódu:
/** * 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); }
Answer
FFT lze provést pouze na omezeném množství dat. Základní matematika je založena na předpokladu, že signál v časové doméně je periodický, tj. Vaše část dat se opakuje v čase. To obvykle vede k velké diskontinuitě na okrajích bloku. Podívejme se na rychlý příklad: velikost FFT = 1 000 bodů, vzorkovací frekvence = 1 000 Hz, frekvenční rozlišení = 1 Hz. Pokud máte sinusovou vlnu 10 Hz, nemáte diskontinuitu, protože do vašeho okna FFT se vejde přesně 10 období. a hodnoty (a deriváty) na okrajích jsou stejné. FFT tohoto siganlu bude nula s výjimkou jediné hodnoty v koši č. 10. Toto funguje stejně dobře pro sinusovou vlnu 11 Hz. U sinusové vlny 10,3 HZ však skončíte s velkou diskontinuitou a FFT bude mít energii ve všech zásobnících s maximem kolem 10 nebo 11 a poté „sukně“, které se odvalují do stran. Malá změna frekvence má tedy za následek masivní změnu obrazu FFT.
K tomu se používá okénkování: Windows se ujistěte, že data na okrajích jsou nulová, takže zde není žádná diskontinuita. Násobení v časové doméně je však konvoluce ve frekvenční doméně, což má za následek rozšíření spektrálních čar a také postranních laloků. Volba okna řídí kompromisy mezi šířkou hlavního laloku a roztečí a výškou bočního laloku. Požadavky specifické pro vaši aplikaci určují, jaké okno se má použít, a existují desítky možností. Hanning je jen jedním z nich. Je to v zásadě „okno volby, pokud nemáte lepší nápady“. Osobně dávám přednost oknům Kaiser, protože mají spojitý parametr, který může ovládat chování oken v širokém rozsahu.
Obecně FFT není skvělá metoda pro detekci výšky tónu. U většiny zvukových signálů NENÍ maximum ve spektru (obvykle harmonické mají vyšší energii), abyste získali slušné rozlišení, potřebujete dlouhé kousky dat, ale díky tomu je algoritmus velmi pomalý a pomalý, aby reagoval na změny. Mnohem lepšími možnostmi jsou smyčky s fázovým vzhledem, smyčky se zpožděním, autokorelace, sledovač max / min, tracker s nulovým přechodem atd.