Tại sao mã phát hiện nhịp này không đăng ký một số nhịp đúng?


38

Tôi đã tạo lớp SoundAnalyzer này để phát hiện nhịp đập trong các bài hát:

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++;
        }
    }
}

Vì một số lý do, nó chỉ phát hiện nhịp đập từ 637 giây đến khoảng 641 giây và tôi không biết tại sao. Tôi biết các nhịp đang được chèn từ nhiều băng tần vì tôi đang tìm các bản sao và dường như nó chỉ định một nhịp cho mỗi giá trị năng lượng tức thời ở giữa các giá trị đó.

Nó được mô hình hóa sau này: http://www.flipcode.com/misc/BeatDetectionAlerskyms.pdf

Vậy tại sao nhịp đập không được đăng ký đúng cách?


2
Bạn có thể đăng một âm mưu về sự phát triển của InstantEnergyList [index + 1] và historyBuffer theo thời gian cho một băng tần không? Hai biểu đồ chồng lên nhau. Điều đó sẽ đưa ra manh mối về vấn đề có thể là gì. Ngoài ra, năng lượng phải là bình phương của độ lớn, đừng quên điều đó.
CeeJay

À vâng, điều đó có thể tiết lộ vấn đề, hãy cho tôi xem nếu tôi có thể bằng cách nào đó tạo ra một số biểu đồ
Quincy

2
Nhưng cốt truyện này chỉ là historyBuffer, hay historyBuffer / numInBuffer * C? Có vẻ như bạn có một chữ C lớn trong đó. Nhìn vào mã, historyBuffer nên có các giá trị tương tự với InstantEnergy, biểu đồ đó chỉ có thể là nếu C quá cao hoặc numInBuffer quá thấp (cách dưới 1), mà tôi đoán không phải vậy.
CeeJay

7
Câu hỏi sẽ không chết ...
Kỹ sư

3
Hãy thử đặt câu hỏi này trên dsp.stackexchange.com
Atav32

Câu trả lời:


7

Tôi đã đâm một nhát vào nó, điều đó thật ngớ ngẩn vì tôi không quen thuộc với các biến đổi Fourier hoặc lý thuyết âm nhạc. Vì vậy, sau một số nghiên cứu tôi không có giải pháp, nhưng tôi thấy một số điều đáng lo ngại:

  • Mã cho Sound và Soundbuffer bị thiếu và có thể dễ dàng là thủ phạm
  • Biến đổi Fourier
    • Tôi không thể tìm thấy cùng một thư viện biến đổi Fourier bằng cách googling không gian tên và tên phương thức, điều đó có nghĩa là mã có thể là tùy chỉnh và có thể là nguồn gốc của vấn đề
    • Việc FastFourier.Calculate mất một mảng ngắn là không bình thường
  • Phương thức GetEnergyList có một Danh sách ref nhưng danh sách này không được sử dụng lại?
  • Ở một số điểm bạn thấy HardSize được mã hóa thành 1024, nhưng không rõ ràng luôn luôn như vậy.
  • Điều đáng lo ngại là nhận xét cho PlaceBeatMarkers lưu ý rằng N nên được chia cho 1024, có thể mã cuộc gọi đã quên để làm điều đó?
  • Tôi rất nghi ngờ về cách historyBuffer bị thao túng trong PlaceMarkers, đặc biệt là khi N được truyền vào và sau đó được sử dụng để thao túng historyBuffer.
  • Nhận xét *// Fill the history buffer with n * instant energy*và mã theo sau không jive.

Sau một thời gian, tôi cảm thấy mã không thực sự được tổ chức tốt và thật lãng phí thời gian để cố gắng sửa chữa. Nếu bạn nghĩ nó đáng giá, bước tiếp theo tôi sẽ làm là:

  1. Chia nó thành phần đơn giản nhất
  2. Viết lại mã theo cách dài dòng nhất, đặt tên cho tất cả các biến ẩn
  3. Viết kiểm tra đơn vị để đảm bảo rằng một phần nhỏ của mã hoạt động chính xác
  4. Thêm một phần mã nhỏ khác và lặp lại cho đến khi bạn có toàn bộ hoạt động đúng

Lời khuyên

  • Bạn có thể muốn làm cho số lượng băng tần cố định để đơn giản hóa logic vòng lặp
  • Đặt tên biến như N, C và B rõ ràng và súc tích, điều này sẽ giúp bạn thấy các lỗi logic dễ dàng hơn
  • Chia các phần lớn của mã thành một số phương thức được gọi là mỗi phương thức thực hiện một bước ngắn gọn của quy trình lớn hơn và có thể có các bài kiểm tra đơn vị được viết để đảm bảo nó hoạt động chính xác.

Tôi là một fan hâm mộ của việc giải câu đố mã, miễn là câu đố là tốt. Do đó tiền thưởng. Tôi rất vui vì bạn đã lấy nó và câu trả lời của bạn để tìm lỗi trong mã là câu trả lời tốt nhất mà câu đố mã có thể nhận được.
Seth Battin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.