Ich arbeite an einem Platformer-Spiel, das Musik mit Beat-Erkennung enthält. Momentan erkenne ich Beats, indem ich überprüfe, ob die aktuelle Amplitude eine historische Stichprobe überschreitet. Dies funktioniert nicht gut mit Musikgenres wie Rock, die eine ziemlich konstante Amplitude haben.
Also habe ich weiter gesucht und Algorithmen gefunden, die den Sound mit FFT in mehrere Bänder aufteilen ... dann habe ich die gefunden aufteilen. Cooley-Tukey FFt-Algorithmus gefunden
Das einzige Problem, das ich habe, ist, dass ich für Audio ziemlich neu bin und keine Ahnung habe, wie ich das verwenden soll, um das Signal in mehrere Signale aufzuteilen.
Meine Frage lautet also:
Wie verwendet man eine FFT, um ein Signal in mehrere Bänder aufzuteilen?
Auch für die Interessierten ist dies mein Algorithmus in c #:
// C = threshold, N = size of history buffer / 1024
public void PlaceBeatMarkers(float C, int N)
{
List<float> instantEnergyList = new List<float>();
short[] samples = soundData.Samples;
float timePerSample = 1 / (float)soundData.SampleRate;
int sampleIndex = 0;
int nextSamples = 1024;
// Calculate instant energy for every 1024 samples.
while (sampleIndex + nextSamples < samples.Length)
{
float instantEnergy = 0;
for (int i = 0; i < nextSamples; i++)
{
instantEnergy += Math.Abs((float)samples[sampleIndex + i]);
}
instantEnergy /= nextSamples;
instantEnergyList.Add(instantEnergy);
if(sampleIndex + nextSamples >= samples.Length)
nextSamples = samples.Length - sampleIndex - 1;
sampleIndex += nextSamples;
}
int index = N;
int numInBuffer = index;
float historyBuffer = 0;
//Fill the history buffer with n * instant energy
for (int i = 0; i < index; i++)
{
historyBuffer += instantEnergyList[i];
}
// If instantEnergy / samples in buffer < instantEnergy for the next sample then add beatmarker.
while (index + 1 < instantEnergyList.Count)
{
if(instantEnergyList[index + 1] > (historyBuffer / numInBuffer) * C)
beatMarkers.Add((index + 1) * 1024 * timePerSample);
historyBuffer -= instantEnergyList[index - numInBuffer];
historyBuffer += instantEnergyList[index + 1];
index++;
}
}
Antworten:
Nun, wenn Ihr Eingangssignal real ist (wie in, ist jedes Sample eine reelle Zahl), ist das Spektrum symmetrisch und komplex. Wenn Sie die Symmetrie ausnutzen, packen normalerweise FFT-Algorithmen das Ergebnis, indem Sie nur die positive Hälfte des Spektrums zurückgeben. Der Realteil jeder Band ist in den geraden Samples und der Imaginärteil in den ungeraden Samples. Oder manchmal werden die Realteile in der ersten Hälfte der Antwort und die Imaginärteile in der zweiten Hälfte zusammengepackt.
Wenn in Formeln X [k] = FFT (x [n]) ist, geben Sie ihm einen Vektor i [n] = x [n] und erhalten eine Ausgabe von o [m]
(obwohl Sie manchmal X [k] = o [k] + j · o [k + K / 2] erhalten, wobei K die Länge Ihres Fensters ist, 1024 in Ihrem Beispiel). Übrigens ist j die imaginäre Einheit sqrt (-1).
Die Größe einer Bande wird als Wurzel des Produkts dieser Bande mit ihrem komplexen Konjugat berechnet:
Und die Energie ist definiert als das Quadrat der Größe.
Wenn wir a = o [2k] und b = o [2k + 1] nennen, erhalten wir
deshalb
Wenn Sie das Ganze abrollen und als Ausgabe des FFT-Algorithmus o [m] erhalten, ist die Energie im Band k:
(Hinweis: Ich habe das Symbol · anstelle des üblichen * verwendet, um Verwechslungen mit dem Konjugationsoperator zu vermeiden.)
Die Frequenz des Bandes k unter der Annahme einer Abtastfrequenz von 44,1 kHz und eines Fensters von 1024 Abtastwerten beträgt
So steht beispielsweise Ihr erstes Band k = 0 für 0 Hz, k = 1 für 43 Hz und das letzte k = 511 für 22 kHz (die Nyquist-Frequenz).
Ich hoffe, dies beantwortet Ihre Frage, wie Sie die Energie des Signals pro Band mithilfe der FFT erhalten.
Nachtrag : Beantworten Sie Ihre Frage im Kommentar und setzen Sie voraus, dass Sie den Code aus dem Link verwenden, den Sie in der Frage gepostet haben (Der Cooley-Tukey-Algorithmus in C).
Mein C ist ein bisschen verrostet (ich programmiere heutzutage hauptsächlich in C ++), aber ich hoffe, dass ich mit diesem Code keinen großen Fehler gemacht habe. Wenn Sie sich für die Energie anderer Bands interessieren, macht es natürlich keinen Sinn, das gesamte Fenster für jede von ihnen zu transformieren, das wäre eine Verschwendung von CPU-Zeit. Führen Sie in diesem Fall die Transformation einmal durch und holen Sie sich alle benötigten Werte von xout.
quelle
Hier finden Sie eine gute Lektüre über die Beat-Erkennung in Spielen.
http://www.badlogicgames.com/wordpress/?p=99
Es ist Teil einer 8-teiligen Blogserie zu diesem Thema.
http://www.badlogicgames.com/wordpress/?category_name=onset-detection-tutorial
quelle
Ich habe das noch nicht gemacht oder viel darüber gelesen, aber meine erste Einstellung ist ungefähr so:
Zunächst müssen Sie eine Fensterfunktion anwenden, um mit der FFT ein zeitabhängiges Spektrum zu erhalten. Der Beat liegt normalerweise in den niedrigeren Frequenzen, also wenden Sie eine andere FFT mit einem größeren Zeitfenster auf die Intensitäten einiger dieser Frequenzen an (beginnen Sie der Einfachheit halber mit nur 1 bei z. B. 100 Hz und prüfen Sie, ob dies zuverlässig genug ist). Finden Sie den Peak in diesem Spektrum und diese Frequenz ist eine Vermutung für den Beat.
quelle