Så jeg har netop revideret min tonehøjde beregningsalgoritme ved hjælp af en harmonisk produktspektrum algoritme. Jeg var bare nysgerrig efter, hvorfor denne forklaring af Harmonic Product Spectrum siger, at du skal implementere et Hanning-vindue i datasættet. Hvad ville effekten være af implementering af andre Window-funktioner på et datasæt (og derefter FFTing det)? Hvilken Windowing-funktion er faktisk den bedste til frekvensregistrering? Her er de relevante metoder, jeg har brugt i min kode:
/** * 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); }
Svar
FFT kan kun udføres over et begrænset stykke data. Den grundlæggende matematik er baseret på antagelsen om, at tidsdomænesignalet er periodisk, dvs. dit stykke data gentages i tide. Det resulterer typisk i en større diskontinuitet ved kanterne af klumpen. Lad os se på et hurtigt eksempel: FFT-størrelse = 1000 point, prøvehastighed = 1000 Hz, frekvensopløsning = 1Hz. Hvis du har en 10 Hz sinusbølge, har du ikke en diskontinuitet, da nøjagtigt 10 perioder passer ind i dit FFT-vindue og værdierne (og derivaterne) ved kanterne er de samme. FFT for denne siganl er nul bortset fra en enkelt værdi i bin # 10. Dette fungerer også for en 11 Hz sinusbølge lige så godt. For 10,3 HZ sinusbølge ender du dog med en masse diskontinuitet, og FFT vil have energi i alle skraldespande med maksimalt omkring 10 eller 11 og derefter “nederdele”, der ruller sporet ud til siderne. Så en lille ændring i frekvens resulterer i en massiv ændring i FFT-billedet.
Windowing bruges til at undgå dette: Windows sørger for, at dataene ved kanterne er nul, så der er ingen diskontinuitet. Multiplikation i tidsdomænet er imidlertid foldning i frekvensdomænet, og det resulterer i udvidelse af spektrale linjer og også i sidelapper. Valget af vindue styrer afvejningerne mellem hovedlappens bredde og sidelappens afstand og højde. Dine applikationsspecifikke krav bestemmer, hvilket vindue du skal bruge, og der er snesevis af valg. Hanning er bare en af dem. Det er dybest set det valgte vindue, hvis du ikke har nogen bedre ideer. Personligt foretrækker jeg Kaiser-vinduer, da de har en kontinuerlig parameter, der kan kontrollere vinduesadfærd over et bredt område.
Generelt er FFT ikke en god metode til tonehøjde-detektion. For de fleste lydsignaler er det maksimale i spektret IKKE det grundlæggende (typisk harmoniske har højere energi). For at få en anstændig opløsning har du brug for lange stykker data, men det gør algoritmen meget langsom og træg til at reagere på ændringer. Meget bedre muligheder er fasekiggede sløjfer, forsinkede sløjfer, autokorrelationer, max / min tracker, zero crossing tracker osv.