เอาชนะการตรวจจับและ FFT


13

ฉันกำลังทำงานกับเกม platformer ซึ่งรวมถึงเพลงที่มีการตรวจจับจังหวะ ขณะนี้ฉันกำลังตรวจจับการเต้นด้วยการตรวจสอบว่าแอมพลิจูดปัจจุบันเกินกว่าตัวอย่างในอดีตหรือไม่ มันใช้งานไม่ได้กับแนวเพลงเช่นร็อคที่มีแอมพลิจูดค่อนข้างคงที่

ดังนั้นฉันจึงค้นหาเพิ่มเติมและพบอัลกอริทึมที่แยกเสียงออกเป็นหลาย ๆ วงโดยใช้ FFT ... จากนั้นฉันก็พบอัลกอริทึม Cooley-Tukey FFt

ปัญหาเดียวที่ฉันมีคือฉันค่อนข้างใหม่กับเสียงและฉันไม่รู้ว่าจะใช้มันอย่างไรเพื่อแยกสัญญาณออกเป็นสัญญาณหลาย ๆ สัญญาณ

ดังนั้นคำถามของฉันคือ:

คุณจะใช้ FFT เพื่อแยกสัญญาณออกเป็นหลายแบนด์ได้อย่างไร?

สำหรับคนที่สนใจนี่คืออัลกอริทึมของฉันใน 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++;
        }
    }

ฉันเดาว่าจุดเริ่มต้นที่ดีคือรายการFFTและDSPของวิกิพีเดีย รายการตรวจจับการเต้นนั้นเบาบาง แต่ลิงก์ไปยังบทความที่ gamedev.net
Tobias Kienzler

คำตอบ:


14

ถ้าสัญญาณอินพุทของคุณเป็นจริง (เหมือนในแต่ละตัวอย่างเป็นจำนวนจริง) สเปกตรัมจะสมมาตรและซับซ้อน การใช้ประโยชน์จากความสมมาตรซึ่งโดยทั่วไปแล้วอัลกอริธึม FFT จะบรรจุผลลัพธ์โดยให้คุณได้ครึ่งคลื่นบวกเท่านั้น ส่วนที่แท้จริงของแต่ละวงอยู่ในตัวอย่างคู่และส่วนจินตภาพในตัวอย่างแปลก ๆ หรือบางครั้งชิ้นส่วนจริงจะถูกรวมเข้าด้วยกันในช่วงครึ่งแรกของการตอบสนองและส่วนจินตภาพในช่วงครึ่งหลัง

ในสูตรถ้า X [k] = FFT (x [n]) คุณให้เวกเตอร์ i [n] = x [n] แล้วเอาท์พุท o [m] จากนั้น

X[k] = o[2k] + j·o[2k+1]

(แม้ว่าบางครั้งคุณจะได้รับ X [k] = o [k] + j · o [k + K / 2] โดยที่ K คือความยาวของหน้าต่างของคุณ 1024 ในตัวอย่างของคุณ) โดยวิธีการที่ j เป็นหน่วยจินตภาพ sqrt (-1)

ขนาดของวงดนตรีจะถูกคำนวณเป็นรากของผลิตภัณฑ์ของวงนี้ด้วยคอนจูเกตที่ซับซ้อน:

|X[k]| = sqrt( X[k] · X[k]* )

และพลังงานถูกกำหนดเป็นกำลังสองของขนาด

ถ้าเราเรียก a = o [2k] และ b = o [2k + 1] เราจะได้

X[k] = a + j·b

ดังนั้น

E[k] = |X[k]|^2 = (a+j·b)·(a-j·b) = a·a + b·b

หากคุณได้รับ o [m] เป็นเอาต์พุตจากอัลกอริธึม FFT พลังงานในแถบ k คือ:

E[k] = o[2k] · o[2k] + o[2k+1] · o[2k+1]

(หมายเหตุ: ฉันใช้สัญลักษณ์·เพื่อระบุการคูณแทน * ตามปกติเพื่อหลีกเลี่ยงความสับสนกับผู้ประกอบการผัน)

ความถี่ของย่านความถี่ k ซึ่งสมมติว่ามีความถี่การสุ่มตัวอย่าง 44.1Khz และหน้าต่าง 1024 ตัวอย่างคือ

freq(k) = k / 1024 * 44100 [Hz]

ตัวอย่างเช่น band แรกของคุณ k = 0 แทน 0 Hz, k = 1 คือ 43 Hz และสุดท้าย k = 511 คือ 22KHz (ความถี่ Nyquist)

ฉันหวังว่านี่จะตอบคำถามของคุณเกี่ยวกับวิธีที่คุณได้รับพลังงานของสัญญาณต่อแบนด์โดยใช้ FFT

ภาคผนวก : ตอบคำถามของคุณในความคิดเห็นและสมมติว่าคุณใช้รหัสจากลิงก์ที่คุณโพสต์ไว้ในคำถาม (อัลกอริทึม Cooley-Tukey ใน C): สมมติว่าคุณมีข้อมูลอินพุตเป็นเวกเตอร์สั้น ๆ :

// len is 1024 in this example.  It MUST be a power of 2
// centerFreq is given in Hz, for example 43.0
double EnergyForBand( short *input, int len, double centerFreq)
{
  int i;
  int band;
  complex *xin;
  complex *xout;
  double magnitude;
  double samplingFreq = 44100.0; 

  // 1. Get the input as a vector of complex samples
  xin = (complex *)malloc(sizeof(struct complex_t) * len);

  for (i=0;i<len;i++) {
    xin[i].re = (double)input[i];
    xin[i].im = 0;
  }

  // 2. Transform the signal
  xout = FFT_simple(xin, len);

  // 3. Find the band ( Note: floor(x+0.5) = round(x) )
  band = (int) floor(centerFreq * len / samplingFreq + 0.5); 

  // 4. Get the magnitude
  magnitude = complex_magnitude( xout[band] );

  // 5. Don't leak memory
  free( xin );
  free( xout );

  // 6. Return energy
  return magnitude * magnitude;
}

My C เป็นบิตสนิม (ฉันส่วนใหญ่เป็นรหัสในปัจจุบัน C ++) แต่ฉันหวังว่าฉันไม่ได้ทำผิดพลาดใหญ่กับรหัสนี้ แน่นอนถ้าคุณสนใจพลังงานของวงดนตรีอื่น ๆ มันไม่สมเหตุสมผลเลยที่จะเปลี่ยนทั้งหน้าต่างสำหรับแต่ละวงนั่นจะทำให้เสียเวลาของ CPU ในกรณีนั้นทำการแปลงหนึ่งครั้งและรับค่าทั้งหมดที่คุณต้องการจาก xout


โอ้ฉันเพิ่งดูรหัสที่คุณเชื่อมโยงมันให้ผลลัพธ์ในรูปแบบ "ซับซ้อน" แล้วและยังให้ฟังก์ชันในการคำนวณขนาดของจำนวนเชิงซ้อน จากนั้นคุณจะต้องคำนวณกำลังสองของขนาดนั้นสำหรับแต่ละองค์ประกอบของเวกเตอร์เอาต์พุตโดยไม่ต้องกังวลกับการเรียงลำดับผลลัพธ์
CeeJay

ตัวอย่างเช่นถ้าฉันมีตัวอย่างทั้งหมด 1024 ตัวอย่างจากหน้าต่าง 0-1024 และฉันได้ค่าเป็นจริงดังนั้นจึงไม่มีส่วนที่ซับซ้อน และฉันต้องการคำนวณพลังงานที่นั่นบนย่านความถี่ 43Hz ฉันจะรวมมันอย่างไร (ฉันจะต้องกลับมาเป็นส่วนหนึ่งจริงส่วน postive) ถ้าคุณสามารถทำมันได้ใน pseudocode บางอย่างที่ฉันจะอยู่ในความลึกของคุณตลอดไปและจากนั้นที่จริงผมอาจจะเข้าใจแนวคิดบิต :)
ควินซี

รหัสที่ฉันเขียนนั้นใช้ไลบรารี C ที่คุณเชื่อมโยงซึ่งมีโครงสร้าง "ซับซ้อน" อยู่แล้ว นี้จะทำให้ unwrapping ผมอธิบายไว้ในคำถามของฉันที่ไม่จำเป็น (และรหัสที่สะท้อนให้เห็นว่า)
CeeJay


0

ฉันยังไม่ได้ทำหรืออ่านอะไรเกี่ยวกับมันด้วยตัวเอง แต่นัดแรกของฉันคืออะไร:

ก่อนอื่นคุณจะต้องใช้ฟังก์ชั่นหน้าต่างเพื่อให้ได้สเปกตรัมที่ขึ้นกับเวลาด้วย FFT จังหวะมักจะอยู่ในความถี่ที่ต่ำกว่าดังนั้นใช้FFT อื่นด้วยหน้าต่างเวลาที่ใหญ่ขึ้นบนความหนาแน่นของความถี่เหล่านี้ (สำหรับความเรียบง่ายเริ่มต้นที่ 1 เพียงเช่นที่ 100 Hz และดูว่ามันน่าเชื่อถือเพียงพอหรือไม่) ค้นหาจุดสูงสุดในสเปกตรัมนี้และความถี่นั้นเป็นการคาดเดาจังหวะ


ไม่ใช่การตรวจจับจังหวะที่แท้จริงที่ฉันมีปัญหา แต่เข้าใจว่าการทำงานของ FFT เป็นอย่างไร ฉันยังใหม่กับการส่งสัญญาณการดำเนินการและสิ่งต่าง ๆ เช่น: "ใช้ฟังก์ชั่นหน้าต่างเพื่อให้ได้คลื่นความถี่ตามเวลาด้วย FFT" ไม่สมเหตุสมผลสำหรับฉัน ขอบคุณอยู่ดี :)
ควินซี
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.