Deshalb habe ich meinen Tonhöhenberechnungsalgorithmus mit einem harmonischen Produktspektrumalgorithmus überarbeitet. Ich war nur neugierig, warum diese Erklärung des harmonischen Produktspektrums besagt, dass Sie ein Hanning-Fenster für den Datensatz implementieren müssen. Welche Auswirkungen hätte die Implementierung anderer Fensterfunktionen auf einen Datensatz (und dessen anschließende FFT)? Welche Fensterfunktion eignet sich eigentlich am besten für die Frequenzerkennung? Hier sind die relevanten Methoden, die ich in meinem Code verwendet habe:
/** * 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); }
Antwort
Die FFT kann nur über einen begrenzten Datenblock durchgeführt werden. Die grundlegende Mathematik basiert auf der Annahme, dass das Zeitbereichssignal periodisch ist, d. H. Ihr Datenblock wird zeitlich wiederholt. Dies führt typischerweise zu einer großen Diskontinuität an den Rändern des Blocks. Schauen wir uns ein kurzes Beispiel an: FFT-Größe = 1000 Punkte, Abtastrate = 1000 Hz, Frequenzauflösung = 1 Hz. Wenn Sie eine 10-Hz-Sinuswelle haben, haben Sie keine Diskontinuität, da genau 10 Perioden in Ihr FFT-Fenster passen und die Werte (und Ableitungen) an den Kanten sind gleich. Die FFT dieses Siganl ist Null, mit Ausnahme eines einzelnen Werts in Bin # 10. Dies funktioniert auch für eine 11-Hz-Sinuswelle. Bei einer Sinuswelle von 10,3 Hz kommt es jedoch zu einer starken Diskontinuität, und die FFT hat in allen Behältern Energie mit maximal 10 oder 11 und dann „Röcken“, die seitlich abrollen. Eine kleine Frequenzänderung führt also zu einer massiven Änderung des FFT-Bildes.
Das Fenster wird verwendet, um dies zu vermeiden: Windows stellt sicher, dass die Daten an den Rändern Null sind, damit keine Diskontinuität auftritt. Die Multiplikation im Zeitbereich ist jedoch eine Faltung im Frequenzbereich und führt zu einer Verbreiterung der Spektrallinien und auch zu Nebenkeulen. Die Wahl des Fensters steuert die Kompromisse zwischen der Breite der Hauptkeule und dem Abstand und der Höhe der Nebenkeule. Ihre anwendungsspezifischen Anforderungen bestimmen, welches Fenster verwendet werden soll, und es gibt Dutzende von Auswahlmöglichkeiten. Hanning ist nur einer von ihnen. Es ist im Grunde das Fenster der Wahl, wenn Sie keine besseren Ideen haben. Persönlich bevorzuge ich Kaiser-Fenster, da sie einen kontinuierlichen Parameter haben, der das Fensterverhalten über einen weiten Bereich steuern kann.
Im Allgemeinen ist FFT keine großartige Methode zur Tonhöhenerkennung. Für die meisten Audiosignale ist das Maximum im Spektrum NICHT die Grundwelle (normalerweise haben Harmonische eine höhere Energie). Um eine anständige Auflösung zu erzielen, benötigen Sie lange Daten, aber das macht den Algorithmus sehr langsam und träge, um auf Änderungen zu reagieren. Viel bessere Optionen sind phasengesteuerte Schleifen, verzögerte Schleifen, Autokorrelationen, Max / Min-Tracker, Nulldurchgangs-Tracker usw.