Ich versuche, die Grundfrequenz eines Signals zu erhalten, das nur eine einzige Tonhöhe hat. Ich habe die Autokorrelationsfunktion mit FFT codiert und bereits das Autokorrelationsergebnis erhalten. Leider weiß ich nicht, wie ich die Grundfrequenz aus dem Autokorrelationsergebnis erhalten soll. Kann mir jemand helfen? Mein Code ist unten:
public double getPitch(double[] buffer, int firstSample, int lastSample, double sampleRate)
{
int lengthOfFFTWindow=lastSample-firstSample;
double[] input_buffer=new double[lengthOfFFTWindow];
DoubleFFT_1D fft = new DoubleFFT_1D(lengthOfFFTWindow);
double[] autocorrelation_values=new double[lengthOfFFTWindow];
double[] fftData = new double[lengthOfFFTWindow * 2];
double max=-1;
double max_i=-1;
//FFT on each sample in each window
for (int i = 0; i < lengthOfFFTWindow; i++) {
// copying audio data to the fft data buffer, imaginary part is 0
fftData[2 * i] = buffer[i+firstSample];
fftData[2 * i + 1] = 0;
}
fft.complexForward(fftData);
for (int i = 0,j=0; i < fftData.length; i += 2,j++) {
// complex numbers -> vectors, so we compute the length of the vector, which is sqrt(realpart^2+imaginarypart^2)
autocorrelation_values[j] = Math.sqrt((fftData[i] * fftData[i]) + (fftData[i + 1] * fftData[i + 1]));
}
fft.complexInverse(fftData, false);
for(int i=0;i<fftData.length;i++)
{
if(max<fftData[i])
{
max=fftData[i];
max_i=i;
}
}
return (max_i * 44100 )/ lengthOfFFTWindow;
}
Ist es richtig, den maximalen Autokorrelationswert dividiert durch 2 als Grundfrequenz zurückzugeben? Dabei bekomme ich immer wieder falsche Antworten.
BEARBEITEN: Ein Beispiel für die Single-Pitch-Testdatei: http://dl.dropbox.com/u/24274475/testfile_A4.wav
Ich sehe ein paar Dinge, die Probleme in Ihrem Code sein könnten.
Um die FFT zur Berechnung einer Autokorrelation zu verwenden, sind zunächst drei Schritte erforderlich:
Ich sehe, wie Sie Schritt 1 und Schritt 2 machen, aber dann machen Sie in Schritt 3 etwas ganz anderes.
Beachten Sie, dass bei einer Autokorrelation die Spitzen die Periode Ihres Signals und nicht die Frequenz anzeigen würden.
Was Ihr Code tatsächlich zu versuchen scheint, ist, die FFT zu nehmen, dann die Bins nach der höchsten Spitze zu durchsuchen und diese als Frequenz des Signals zu nehmen. Sie berechnen auch die IFFT, verwerfen dann aber das Ergebnis dieser Berechnung.
Eine Spitzensuche im Leistungsspektrum kann verwendet werden, um die Signalfrequenz abzuschätzen, aber da haben Sie einen anderen, grundlegenderen Fehler:
for(int i=0;i<autocorrelation_values.length;i++)
{
if(max<autocorrelation_values[i])
{
max=autocorrelation_values[i];
max_i=i;
}
}
return max/2;
Sie max
speichern die maximale Amplitude der Leistungsspektrumwerte (die Sie verwirrend benannt haben autocorrelation_values
). max_i
speichert die Bin-Nummer, wo die maximale Amplitude gefunden wurde. Sie sollten etwas zurückgeben, das max_i
eher auf als auf basiert max
. Sie müssen die Abtastrate und die Anzahl der in der FFT verwendeten Abtastungen verwenden, um die Bin-Nummer in eine tatsächliche Frequenz zu skalieren.
Bearbeiten Ich würde auch empfehlen, nur die erste Hälfte des Leistungsspektrums nach Spitzen zu durchsuchen.
for(int i=0; i < 0.5 * autocorrelation_values.length; i++)
...
Die höheren Bins entsprechen einem Alias des Spektrums in den negativen Frequenzen, enthalten also keine neuen Informationen.
max_i * samplingrate / lenthOfFFTWindow
. Ich glaube nicht, dass Sie den Faktor 2 brauchen. Sobald Sie sich dem richtigen Ergebnis nähern, sollten Sie jedoch sofort sehen können, ob Sie 2x daneben liegen.autocorrelation_values
) zu zeichnen, und sehen Sie, ob die höchste Spitze wirklich dort ist, wo Sie sie vermuten.autocorrelation_values
... stellen Sie sicher, dass es das ist, was Sie denken ... Audacity spielt möglicherweise eine Menge Tricks, um eine schöne Spektrumanzeige zu erstellen, und versucht nicht, mathematisch korrekt zu sein.Autokorrelation ist eine Möglichkeit, die dominante Frequenz eines Signals zu finden, aber ich verstehe nicht, was eine FFT damit zu tun hat. Die Autokorrelation erzeugt Spitzen mit der Periode aller starken Frequenzkomponenten. Wenn Sie dann die FFT davon nehmen, um die Frequenz dieser Spitzen zu finden, können Sie genauso gut zuerst die FFT des Originalsignals nehmen.
Anstatt uns Code zu zeigen, zeigen Sie uns die Daten in verschiedenen Phasen Ihres Prozesses. Die Details des Codes sind Ihr Problem und sind von den konzeptionellen Prozessen des Durchlaufens der verschiedenen Faltungen, Filter oder was auch immer getrennt.
Sie sagen, Ihr Signal hat nur eine einzige Tonhöhe, was bedeutet, dass es eine reine Sinuswelle ist. In diesem Fall sehe ich den Vorteil eines Autokorrelationspasses wirklich nicht. Sie können die Periode direkt finden, indem Sie sich die Zeit zwischen den Nulldurchgängen ansehen.
In der Vergangenheit musste ich die Grundfrequenz von meist sich wiederholenden Signalen mit erheblichem Rauschen finden. Normalerweise habe ich mehrere Stufen der Tiefpassfilterung angewendet. Ein großer Vorteil der digitalen Filterung besteht darin, dass kleinere Signale aus einem Filter nicht zu einem geringeren Signal-Rausch-Verhältnis führen, solange Sie die erforderlichen Bits am unteren Ende hinzufügen. Die Verwendung von Gleitkommazahlen erledigt dies beispielsweise automatisch. Sie können dann ein Signal aggressiv tiefpassfiltern, so dass es analog nur µV wäre, aber am Ende immer noch die gleichen sinnvollen Bits übrig bleiben.
Jeder LPF-Durchgang dämpft die Harmonischen relativ zur Grundwelle. Nach genügend Durchgängen bleibt Ihnen hauptsächlich das Grundlegende. Sobald Sie die Harmonischen ausreichend gedämpft haben, um nur zwei Nulldurchgänge pro Zyklus zu garantieren, betrachten Sie die Nulldurchgangsperiode, wenden möglicherweise eine kleine Tiefpassfilterung auf aufeinanderfolgende an und leiten daraus die Frequenz ab.
Nachdem Sie nun einige Daten bereitgestellt haben, können wir sehen, was wirklich vor sich geht:
Es sieht so aus, als hätten Sie ein Signal von etwa 440 Hz, aber dies ist eindeutig weit von einer "einzelnen Tonhöhe" entfernt, da die Form weit von einem Sinus entfernt ist. Schon bei der Betrachtung können wir sehen, dass die zweite Harmonische besonders stark ist. Es kann so stark sein, dass dieser "Ton" als 880 Hz statt als Grundton von 440 Hz wahrgenommen wird.
Was soll in diesem Fall die Antwort sein, 440 Hz oder 880 Hz? Bei ausreichender Tiefpassfilterung erhalten Sie schließlich hauptsächlich den Grundton, und das Messen von 440 Hz sollte nicht so schwierig sein. Wenn Sie die Antwort auf den möglicherweise wahrnehmbaren Ton von 880 Hz haben möchten, wird die Sache viel komplizierter. Eine Möglichkeit wäre, die Fundamentaldaten in allen Fällen zu identifizieren. Sobald Sie das haben, ist es einfach, die relative Amplitude der ersten paar Harmonischen zu finden. Dann können Sie anhand der Stärke dieser Harmonischen entscheiden, ob Sie eine davon oder die Grundschwingung melden möchten.
Warum machen Sie es nicht auf die altbewährte Weise, indem Sie eine zeitverzögerte Version des Signals verwenden. Multipliziere das Signal mit der verzögerten Version von sich selbst und speichere das Ergebnis. Für ein bestimmtes Zeitfenster mit (sagen wir) 1000 Samples müssen Sie das größte Ergebnis der einzelnen Multiplikationen speichern.
Testen Sie dann erneut mit einer Reihe verschiedener Verzögerungen. Das numerisch größte Ergebnis entspricht der Periode des Signals. Beachten Sie, dass die Verzögerung, sobald sie die „Periode“ des Signals gefunden hat, auch eine „Bild“-Periode mit der doppelten Verzögerung findet (weil die Signalspitzen wieder zusammenfallen).
Verwenden Sie alternativ einen Nulldurchgangsdetektor und messen Sie die Zeit. Sie sagten, das Signal hat nur eine einzige Tonhöhe, also sollte dies vollkommen in Ordnung sein - achten Sie auf Nulldurchgangsrauschen, indem Sie einige Zyklen mitteln - je mehr, desto besser.
Ich möchte die Grundfrequenz mit der Autokorrelationsmethode finden. Ich habe Ihren Code gelesen, aber ich denke, dass der letzte Teil des Codes so aussehen könnte:
int max=0;
for (int i=0; i < autocorrelation_value.length; i++) {
if (autocorrelation_value[i]>autocorrelation_value[max]) {
max = i;
}
return 1/autocorrelation_value[max]
}
da das Maximum der Autokorrelation die Grundperiode ist und ich daher in der "Rückgabe" die Umkehrung des Maximums der Autokorrelation berechne.
Und nachdem ich nicht verstehe, was "max_i" bedeutet.
geometrisch
Olin Lathrop
Ben Voigt
Kortuk