Saya sedang mengerjakan game platformer yang mencakup musik dengan deteksi ketukan. Saat ini saya mendeteksi ketukan dengan memeriksa kapan amplitudo saat ini melebihi sampel historis. Ini tidak bekerja dengan baik dengan genre musik, seperti rock, yang memiliki amplitudo yang cukup mantap.
Jadi saya melihat lebih jauh dan menemukan algoritma yang membelah suara menjadi beberapa band menggunakan FFT ... kemudian saya menemukan algoritma Cooley-Tukey FFt
Satu-satunya masalah yang saya alami adalah bahwa saya cukup baru untuk audio dan saya tidak tahu bagaimana menggunakannya untuk membagi sinyal menjadi beberapa sinyal.
Jadi pertanyaan saya adalah:
Bagaimana Anda menggunakan FFT untuk membagi sinyal menjadi beberapa band?
Juga untuk orang-orang yang tertarik, ini adalah algoritma saya di 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++;
}
}
Jawaban:
Nah, jika sinyal input Anda nyata (seperti, setiap sampel adalah bilangan real), spektrumnya akan simetris dan kompleks. Mengeksploitasi simetri, biasanya algoritma FFT mengemas hasilnya dengan memberikan Anda kembali hanya setengah positif dari spektrum. Bagian nyata dari masing-masing band adalah dalam sampel genap dan bagian imajiner dalam sampel aneh. Atau kadang-kadang bagian-bagian yang nyata dikemas bersama di paruh pertama respons dan bagian imajiner di babak kedua.
Dalam rumus, jika X [k] = FFT (x [n]), Anda memberinya vektor i [n] = x [n], dan mendapatkan output o [m], maka
(meskipun terkadang Anda mendapatkan X [k] = o [k] + j · o [k + K / 2], di mana K adalah panjang jendela Anda, 1024 dalam contoh Anda). Omong-omong, j adalah unit imajiner, sqrt (-1).
Besarnya pita dihitung sebagai akar dari produk pita ini dengan konjugat kompleksnya:
Dan energi didefinisikan sebagai kuadrat besarnya.
Jika kita memanggil a = o [2k] dan b = o [2k + 1], kita dapatkan
karena itu
Membuka gulungan semuanya, jika Anda mendapatkan o [m] sebagai output dari algoritma FFT, energi dalam band k adalah:
(Catatan: Saya menggunakan simbol · untuk menunjukkan perkalian alih-alih yang biasa * untuk menghindari kebingungan dengan operator konjugasi)
Frekuensi pita k, dengan asumsi frekuensi pengambilan sampel 44.1KHz dan jendela 1024 sampel, adalah
Jadi, misalnya, band pertama Anda k = 0 mewakili 0 Hz, k = 1 adalah 43 Hz, dan yang terakhir k = 511 adalah 22KHz (frekuensi Nyquist).
Saya harap ini menjawab pertanyaan Anda tentang bagaimana Anda mendapatkan energi dari sinyal per band menggunakan FFT.
Tambahan : Menjawab pertanyaan Anda di komentar, dan dengan asumsi Anda menggunakan kode dari tautan yang Anda poskan dalam pertanyaan (Algoritma Cooley-Tukey dalam C): Katakanlah Anda memiliki data input Anda sebagai vektor int pendek:
C saya agak berkarat (saya kebanyakan mengkode dalam C ++ saat ini), tapi saya harap saya tidak membuat kesalahan besar dengan kode ini. Tentu saja jika Anda tertarik pada energi dari band lain, tidak masuk akal untuk mengubah seluruh jendela untuk masing-masing, itu akan membuang-buang waktu CPU. Dalam hal itu lakukan transformasi sekali dan dapatkan semua nilai yang Anda butuhkan dari xout.
sumber
Berikut ini adalah bacaan yang bagus tentang deteksi ketukan dalam gim.
http://www.badlogicgames.com/wordpress/?p=99
Ini adalah bagian dari seri blog 8 bagian tentang masalah ini.
http://www.badlogicgames.com/wordpress/?category_name=onset-detection-tutorial
sumber
Saya belum melakukan ini atau membaca banyak tentang hal itu sendiri, tetapi kesempatan pertama saya adalah seperti ini:
Pertama-tama, Anda harus menerapkan fungsi jendela untuk mendapatkan spektrum tergantung waktu dengan FFT. Ketukan biasanya terletak pada frekuensi yang lebih rendah, jadi terapkan FFT lain dengan jendela waktu yang lebih besar pada intensitas beberapa frekuensi ini (untuk kesederhanaan mulailah dengan hanya 1 pada misalnya 100 Hz dan lihat apakah itu cukup dapat diandalkan). Temukan puncak dalam spektrum ini dan frekuensi itu adalah perkiraan untuk ketukan.
sumber