이 비트 감지 코드가 일부 비트를 올바르게 등록하지 못하는 이유는 무엇입니까?


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

Ahh 네, 문제를 밝힐 수도 있습니다. 어떻게 든 그래프를 만들 수 있는지 봅시다
Quincy

2
그러나이 플롯은 historyBuffer 또는 historyBuffer / numInBuffer * C입니까? 거기에 거대한 C가있는 것 같습니다. 코드를 볼 때 historyBuffer는 instantEnergy와 비슷한 값을 가져야합니다. 그래프는 C가 너무 높거나 numInBuffer가 너무 낮을 때만 가능합니다 (1 미만). 그렇지 않은 것 같습니다.
CeeJay

7
죽지 않을 질문 ...
엔지니어

답변:


7

나는 푸리에 변환이나 음악 이론에 익숙하지 않기 때문에 바보가되었습니다. 따라서 몇 가지 연구를 한 후에 해결책이 없지만 몇 가지 문제가 있습니다.

  • 사운드 및 사운드 버퍼 코드가 누락되어 쉽게 범인이 될 수 있습니다.
  • 푸리에 변환
    • 네임 스페이스와 메서드 이름을 검색하여 동일한 푸리에 변환 라이브러리를 찾을 수 없습니다. 즉, 코드가 사용자 정의되어 문제의 원인이 될 수 있습니다.
    • FastFourier.Calculate가 짧은 배열을 취한다는 사실은 드문 경우입니다.
  • GetEnergyList 메소드는 참조 목록을 가져 오지만이 목록은 다시 사용되지 않습니까?
  • 여러 곳에서 SampleSize가 1024로 하드 코딩 된 것을 볼 수 있지만 항상 그런 것은 분명하지 않습니다.
  • PlaceBeatMarkers에 대한 의견은 N을 1024로 나눠야한다고 지적하고 있습니다. 아마도 호출 코드는 잊어 버렸습니까?
  • 히스토리 버퍼가 PlaceMarkers에서 조작되는 방식, 특히 N이 전달되어 historyBuffer를 조작하는 데 사용되는 방식이 매우 의심됩니다.
  • 다음에 나오는 주석 *// Fill the history buffer with n * instant energy*과 코드는 움직이지 않습니다.

잠시 후 코드가 실제로 제대로 구성되지 않았고 수정하려고하는 데 시간이 낭비되는 느낌이 들었습니다. 가치가 있다고 생각되면 다음 단계는 다음과 같습니다.

  1. 가장 간단한 부분으로 분류하십시오.
  2. 가장 자세한 방법으로 코드를 다시 작성하고 모든 숨겨진 변수의 이름을 지정하십시오.
  3. 코드의 작은 부분이 올바르게 작동하는지 확인하기 위해 단위 테스트 작성
  4. 코드의 다른 작은 섹션을 추가하고 모든 것이 올바르게 작동 할 때까지 반복하십시오.

  • 루프 로직을 단순화하기 위해 밴드 수를 고정 시키려고 할 수 있습니다.
  • 명확하고 간결한 N, C 및 B와 같은 좋은 이름을 지정하면 논리적 오류를 쉽게 볼 수 있습니다.
  • 큰 코드 섹션을 여러 개의 호출 된 메소드로 나누면 각각 더 큰 프로세스의 작은 간결한 단계를 수행하고 올바르게 작동하는지 확인하기 위해 단위 테스트를 작성할 수 있습니다.

나는 수수께끼가 좋은 한 코드 수수께끼를 해결하는 팬입니다. 따라서 현상금. 나는 당신이 그것을 가져서 기쁘고 코드에서 오류를 찾는 당신의 대답은 코드 수수께끼가 얻을 수있는 가장 좋은 대답입니다.
Seth Battin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.