ทำไมรหัสการตรวจจับการเต้นผิดเพี้ยนนี้จึงล้มเหลวในการบันทึกการเต้นบางอย่างอย่างถูกต้อง?


38

ฉันสร้างคลาส SoundAnalyzer นี้เพื่อตรวจจับการเต้นในเพลง:

class SoundAnalyzer
{
    public SoundBuffer soundData;
    public Sound sound;
    public List<double> beatMarkers = new List<double>();

    public SoundAnalyzer(string path)
    {
        soundData = new SoundBuffer(path);
        sound = new Sound(soundData);
    }

    // C = threshold, N = size of history buffer / 1024  B = bands
    public void PlaceBeatMarkers(float C, int N, int B)
    {
        List<double>[] instantEnergyList = new List<double>[B];
        GetEnergyList(B, ref instantEnergyList);
        for (int i = 0; i < B; i++)
        {
            PlaceMarkers(instantEnergyList[i], N, C);
        }
        beatMarkers.Sort();
    }

    private short[] getRange(int begin, int end, short[] array)
    {
        short[] result = new short[end - begin];
        for (int i = 0; i < end - begin; i++)
        {
            result[i] = array[begin + i];
        }
        return result;
    }

    // get a array of with a list of energy for each band
    private void GetEnergyList(int B, ref List<double>[] instantEnergyList)
    {
        for (int i = 0; i < B; i++)
        {
            instantEnergyList[i] = new List<double>();
        }
        short[] samples = soundData.Samples;

        float timePerSample = 1 / (float)soundData.SampleRate;
        int sampleIndex = 0;
        int nextSamples = 1024;
        int samplesPerBand = nextSamples / B;

        // for the whole song
        while (sampleIndex + nextSamples < samples.Length)
        {
            complex[] FFT = FastFourier.Calculate(getRange(sampleIndex, nextSamples + sampleIndex, samples));
            // foreach band
            for (int i = 0; i < B; i++)
            {
                double energy = 0;
                for (int j = 0; j < samplesPerBand; j++)
                    energy += FFT[i * samplesPerBand + j].GetMagnitude();

                energy /= samplesPerBand;
                instantEnergyList[i].Add(energy);

            }

            if (sampleIndex + nextSamples >= samples.Length)
                nextSamples = samples.Length - sampleIndex - 1;
            sampleIndex += nextSamples;
            samplesPerBand = nextSamples / B;
        }
    }

    // place the actual markers
    private void PlaceMarkers(List<double> instantEnergyList, int N, float C)
    {
        double timePerSample = 1 / (double)soundData.SampleRate;
        int index = N;
        int numInBuffer = index;
        double 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++;
        }
    }
}

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

มันเป็นแบบอย่างหลังจากนี้: http://www.flipcode.com/misc/BeatDetectionAlgorithms.pdf

ดังนั้นทำไมเต้นไม่ลงทะเบียนอย่างถูกต้อง?


2
คุณสามารถโพสต์เนื้อเรื่องวิวัฒนาการของ instantEnergyList [index + 1] และ historyBuffer เมื่อเวลาผ่านไปสำหรับหนึ่งวงหรือไม่? กราฟสองรายการซ้อนทับกัน นั่นจะให้เบาะแสเกี่ยวกับปัญหาที่อาจเกิดขึ้น พลังงานต้องเป็นกำลังสองของขนาดอย่าลืมว่า
CeeJay

อ่าใช่ที่อาจเปิดตัวปัญหาให้ฉันดูว่าฉันอย่างใดสามารถทำให้กราฟบาง
ควินซี

2
แต่พล็อตนี้เป็นเพียง historyBuffer หรือ historyBuffer / numInBuffer * C ดูเหมือนว่าคุณมี C ขนาดใหญ่อยู่ในนั้น การดูรหัส historyBuffer ควรมีค่าใกล้เคียงกับ instantEnergy กราฟนั้นสามารถทำได้ก็ต่อเมื่อ C สูงเกินไปหรือ numInBuffer ต่ำเกินไป (ทางด้านล่าง 1) ซึ่งฉันคิดว่ามันไม่ใช่อย่างนั้น
CeeJay

7
คำถามที่จะไม่ตาย ...
วิศวกร

3
ลองถามคำถามนี้กับdsp.stackexchange.com
Atav32

คำตอบ:


7

ฉันเอาไปแทงซึ่งมันเป็นใบ้เพราะฉันไม่คุ้นเคยกับการแปลงฟูริเยร์หรือทฤษฎีดนตรี ดังนั้นหลังจากการศึกษาฉันไม่ได้มีวิธีแก้ปัญหา แต่ฉันเห็นสิ่งต่าง ๆ ที่น่าเป็นห่วง:

  • รหัสสำหรับ Sound and Soundbuffer หายไปและอาจเป็นผู้ร้ายได้ง่าย
  • การแปลงฟูริเยร์
    • ฉันไม่สามารถหาฟูเรียร์ที่แปลงไลบรารี่โดย googling เนมสเปซและชื่อเมธอดได้ซึ่งหมายความว่ารหัสอาจจะกำหนดเองและอาจเป็นสาเหตุของปัญหา
    • ความจริงที่ว่า FastFourier.Calculate ใช้เวลาช่วงสั้น ๆ นั้นผิดปกติ
  • วิธีการ GetEnergyList ใช้รายการอ้างอิง แต่รายการนี้ไม่ได้ใช้อีกหรือไม่
  • ในหลาย ๆ จุดที่คุณเห็น SampleSize ฮาร์ดโค้ดถึง 1024 แต่ไม่ชัดเจนว่าเป็นตัวพิมพ์ใหญ่เสมอ
  • เป็นเรื่องที่หนักใจหรือเปล่าที่ความคิดเห็นของ PlaceBeatMarkers ตั้งข้อสังเกตว่า N ควรถูกหารด้วย 1024 บางทีรหัสโทรศัพท์อาจลืมที่จะทำอย่างนั้นหรือ
  • ฉันสงสัยอย่างมากเกี่ยวกับวิธีการจัดการประวัติของ Buffer ใน PlaceMarkers โดยเฉพาะอย่างยิ่งเมื่อมีการส่งผ่าน N และใช้เพื่อจัดการกับประวัติของ Buffer
  • ความคิดเห็น*// Fill the history buffer with n * instant energy*และรหัสที่ตามมาไม่ได้หลอกลวง

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

  1. ทำลายมันลงไปสู่ส่วนที่ง่ายที่สุด
  2. เขียนโค้ดในวิธีที่ละเอียดที่สุดตั้งชื่อตัวแปรที่ซ่อนอยู่ทั้งหมด
  3. เขียนการทดสอบหน่วยเพื่อให้แน่ใจว่าส่วนเล็ก ๆ ของรหัสทำงานอย่างถูกต้อง
  4. เพิ่มอีกส่วนเล็ก ๆ ของรหัสและทำซ้ำจนกว่าคุณจะทำงานถูกต้อง

เคล็ดลับ

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

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