Dus ik heb zojuist mijn algoritme voor het berekenen van de toonhoogte herzien met behulp van een algoritme voor het harmonische productspectrum. Ik was gewoon benieuwd waarom deze uitleg van Harmonic Product Spectrum stelt dat je een Hanning Window moet implementeren op de dataset. Wat zou het effect zijn van het implementeren van andere Window-functies op een dataset (en deze vervolgens te FFTen)? Welke Windowing-functie is eigenlijk het beste voor frequentiedetectie? Hier zijn de relevante methoden die ik in mijn code heb gebruikt:
/** * 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); }
Antwoord
De FFT kan alleen worden uitgevoerd over een beperkt aantal gegevens. De basisberekening is gebaseerd op de aanname dat het tijddomeinsignaal periodiek is, d.w.z. uw brok gegevens wordt in de tijd herhaald. Dat resulteert typisch in een grote discontinuïteit aan de randen van de brok. Laten we eens kijken naar een snel voorbeeld: FFT-grootte = 1000 punten, samplefrequentie = 1000 Hz, frequentieresolutie = 1 Hz. Als je een sinusgolf van 10 Hz hebt, heb je geen discontinuïteit omdat er precies 10 perioden in je FFT-venster passen en de waarden (en afgeleiden) aan de randen zijn hetzelfde. De FFT van deze siganl is nul, behalve voor een enkele waarde in bin # 10. Dit werkt ook net zo goed voor een sinusgolf van 11 Hz. Echter, voor 10,3 HZ sinusgolf krijg je veel discontinuïteit en de FFT zal energie hebben in alle bakken met een maximum van rond de 10 of 11 en dan “rokken” die naar de zijkanten wegrollen. Een kleine verandering in frequentie resulteert dus in een enorme verandering in het FFT-beeld.
Windowing wordt gebruikt om dit te voorkomen: Windows zorgt ervoor dat de gegevens aan de randen nul zijn, zodat er geen discontinuïteit is. Vermenigvuldiging in het tijdsdomein is echter convolutie in het frequentiedomein en dat resulteert in verbreding van spectraallijnen en ook in zijlobben. De keuze van het venster bepaalt de afweging tussen de breedte van de hoofdlob en de afstand tussen de zijlobben en de hoogte. Uw toepassingsspecifieke vereisten bepalen welk venster u moet gebruiken en er zijn tientallen keuzes. Hanning is er slechts een van. Het “is eigenlijk” het raam van uw keuze als u “geen betere ideeën hebt”. Persoonlijk geef ik de voorkeur aan Kaiser-vensters omdat ze een continue parameter hebben die het venstergedrag over een breed bereik kan regelen.
Over het algemeen is FFT geen geweldige methode voor pitchdetectie. Voor de meeste audiosignalen is het maximum in het spectrum NIET de fundamentele (meestal hebben harmonischen een hogere energie), om een fatsoenlijke resolutie te krijgen heb je een lange hoeveelheid gegevens nodig, maar dat maakt het algoritme erg traag en traag om op veranderingen te reageren. Veel betere opties zijn phase-look loops, delay-look loops, autocorrelaties, max / min tracker, zero crossing tracker, etc.