Wie erhält man die Grundfrequenz eines Signals mithilfe der Autokorrelation?

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 schlage vor, dsp.stackexchange.com wäre ein guter Ort für Ihre Fragen
Sie sagen jetzt, dies sei eine Aufnahme einer Geigennote. Das macht es irreführend zu sagen, dass es eine "einzelne Steigung" hat. Es wird sicherlich Obertöne haben. Können Sie einen Link zu einer WAV-Datei oder so etwas bereitstellen? Dann können wir sehen, wie die Roheingabe wirklich aussieht.
Das Cepstrum ist auch nützlich für die Tonhöhenerkennung.
@geometrisch im Allgemeinen sollten Sie es für die Migration kennzeichnen, nicht die Site vorschlagen, da dies zu Problemen mit Crossposting führt.

Antworten (4)

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:

  1. Transformieren Sie das Eingangssignal, F R ( F ) = F F T ( X [ T ] )
  2. Berechnen Sie das Leistungsspektrum, S ( F ) = F R ( F ) F R ( F )
  3. inverse Transformation, um die Autokorrelation zu erhalten, R [ τ ] = ICH F F T ( S ( F ) )

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 maxspeichern die maximale Amplitude der Leistungsspektrumwerte (die Sie verwirrend benannt haben autocorrelation_values). max_ispeichert die Bin-Nummer, wo die maximale Amplitude gefunden wurde. Sie sollten etwas zurückgeben, das max_ieher 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.

Vielen Dank für Ihre Anregungen! Soll ich in diesem Fall die Abtastrate/(max_i*2) zurückgeben?
@Sakura, ich denke, es sollte sein 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.
Ich habe den Code entsprechend aktualisiert, aber ich bekomme immer 18935 Hz statt 440 Hz. Es ist mehr als doppelt so groß. Habe ich etwas falsch gemacht?
Wenn Sie die von Ihnen verlinkte WAV-Datei testen, klingt es für mich überhaupt nicht wie ein einzelner Ton. Vielleicht stimmen Ihre Daten nicht mit Ihren Annahmen überein? Versuchen Sie, das Leistungsspektrum (das Array, das Sie nennen autocorrelation_values) zu zeichnen, und sehen Sie, ob die höchste Spitze wirklich dort ist, wo Sie sie vermuten.
Ich zeichne das Spektrum mit Audacity auf und erhalte die Grundfrequenz (440 Hz) als eine der Spitzen.
Ist es der höchste Gipfel? Weil Ihr Code nur die höchste Spitze findet.
Es ist der zweithöchste Gipfel. Die höchste Spitze liegt bei 880 Hz statt 440 Hz.
Als nächstes würde ich Ihr Array ausgeben und plotten 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.

Hinzugefügt:

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.

Mein Signal hat nur 1 Tonhöhe (hat aber auch mehrere Frequenzen). Es ist keine reine Sinuswelle. Ich habe eine Musiknote aufgenommen, indem ich auf der Geige gespielt habe.
@Sakura: Dann ist es irreführend zu sagen, dass es eine "einzelne Tonhöhe" hat. Wenn Ihr Signal kein reiner Sinus ist, dann hat es per Definition Oberwellen. Versuchen Sie es auf jeden Fall mit der LPF-Methode. Es sollte nicht zu viele Durchgänge benötigen, um die Oberwellen so zu reduzieren, dass Sie zuverlässig nur zwei Nulldurchgänge pro Zyklus erhalten.
LPF==Tiefpassfilter?
@Sakura: Ja, "LPF" ist die Abkürzung für "Tiefpassfilter".
Was ist die LPF-Methode?
@Sakura: Was ich in den letzten beiden Absätzen beschrieben habe.
Die Note liegt bei etwa A=440Hz. Eine Geige kann keine 44 Hz erzeugen. Die zweite Harmonische ist normalerweise stärker als die erste in vielen Musikinstrumenten, einschließlich der Geige. Dies führt aufgrund der harmonischen Struktur nicht zu einer falschen Wahrnehmung der Tonhöhe.
@EJP: Doh! Ich weiß nicht, wie ich es geschafft habe, so einen Dezimalpunkt zu verschieben. Sie haben Recht, die Grafik zeigt deutlich 440 Hz Grundfrequenz, nicht 44 Hz. Ich habe die Antwort bearbeitet, um das zu beheben.
Der „mögliche Wahrnehmungston“ ist immer noch 440Hz. Das Ohr führt selbst eine FFT durch und würde die 880- und 1320-Harmonischen als separate Töne wahrnehmen, wenn die 440 Hz ausreichend unterdrückt würden, nicht als Teil einer "wahrnehmbaren" 880 Hz.

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.

Inwiefern ist das, was Sie in den ersten beiden Absätzen beschreiben, keine Autokorrelation?
@Olin Lathrop - Es ist Autokorrelation - ich habe es gesagt, damit er seinen eigenen Algorithmus entwerfen kann, wenn er möchte.
Ich habe die Frage so verstanden, dass er bereits weiß, was Autokorrelation ist, aber nicht weiß, wie er das Ergebnis anwenden oder für das Gesamtziel der Bestimmung der Grundfrequenz verwenden soll.
@OlinLathrop - Ich verstand sein "Verlassen" auf FFT nicht, also nahm ich an, dass er nicht wusste, was die Vor- und Nachteile waren, und dachte, ich würde es ein bisschen buchstabieren.
@OlinLathrop - Ich sehe, Sie haben vielleicht dasselbe gedacht.
Ja, ich verstehe nicht, was FFT hier mit irgendetwas zu tun hat, wenn ich versuche, die Autokorrelationsmethode zu verwenden.
Hallo. Ich habe FFT verwendet, um die Autokorrelation zu berechnen, damit die Geschwindigkeit des Algorithmus schneller ist. Durch Multiplizieren des Signals mit seinem Zeitverschiebungssignal wird die Geschwindigkeit viel langsamer.

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.