Neden bu atım algılama kodu bazı atımları düzgün şekilde kaydedemiyor?


38

Bu SoundAnalyzer sınıfını şarkılardaki vuruşları tespit etmek için yaptım:

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

Nedense sadece 637 saniyeden 641 saniyeye kadar atışları tespit ediyor ve nedenini bilmiyorum. Yinelemeleri bulduğum için atımların birden fazla banttan eklendiğini biliyorum ve bu değerler arasındaki her anlık enerji değerine bir atım atadığı görülüyor.

Bundan sonra modellenmiştir: http://www.flipcode.com/misc/BeatDetectionAlgorithms.pdf

Peki neden atımlar düzgün şekilde kaydedilmiyor?


2
İnstantEnergyList [index + 1] ve historyBuffer'ın zaman içindeki değişimini tek bir grup için yayınlayabilir misiniz? İki grafik birbirinin üstüne yerleştirildi. Bu sorunun ne olabileceği hakkında ipucu verir. Ayrıca, enerji büyüklüğün karesi olmalı, bunu unutma.
CeeJay

Ahh evet bu sorunu çözebilir, bakalım bir şekilde bazı grafikler yapabilir miyim
Quincy

2
Ama bu arsa sadece historyBuffer veya historyBuffer / numInBuffer * C? İçeride kocaman bir C varmış gibi görünüyor. Koduna bakıldığında, historyBuffer, instantEnergy ile benzer değerlere sahip olmalıdır, bu grafik yalnızca C çok yüksek veya numInBuffer çok düşük olduğunda olabilir (1'in altında), ki durum böyle değil.
CeeJay

7
Ölmeyecek soru ...
Mühendis

3
Bu soruyu soran deneyin dsp.stackexchange.com
Atav32

Yanıtlar:


7

Bunu aptalca kullandım, aptaldı çünkü Fourier dönüşümlerine ya da müzik teorisine aşina değildim. Bu yüzden, bazı çalışmalardan sonra bir çözümüm yok, ama birkaç sorunlu şey görüyorum:

  • Ses ve Ses Geliştiricisinin kodu eksik ve kolayca suçlu olabilir
  • Fourier Dönüşümü
    • Aynı Fourier dönüşüm kitaplığını ad alanını ve yöntem adlarını çevirerek bulamadım, bu kodun özel olabileceği ve sorunun kaynağı olabileceği anlamına gelir
    • FastFourier.Calculate'in kısa bir dizi alması olağandışı
  • GetEnergyList yöntemi bir ref List alır, ancak bu liste bir daha kullanılmaz mı?
  • Birkaç noktada, 1024'e kodlanmış SampleSize kodunu görüyorsunuz, ancak bunun her zaman böyle olduğu kesin değil.
  • PlaceBeatMarkers'ın yorumunun N'nin 1024'e bölünmesi gerektiğini, belki de arama kodunun bunu unuttuğunu unutması rahatsız edici.
  • HistoryBuffer'ın PlaceMarkers'ta manipüle edilme biçiminden çok şüpheliyim, çünkü özellikle N geçti ve daha sonra historyBuffer'ı değiştirmek için kullanıldı.
  • Yorum *// Fill the history buffer with n * instant energy*ve ardından gelen kod geçerli değil.

Bir süre sonra, kodun gerçekten iyi organize edilmediğini ve düzeltmeye çalışmak zaman kaybı olacağını hissediyorum. Buna değer olduğunu düşünüyorsanız, atmam gereken bir sonraki adım:

  1. En basit parçaya ayırın
  2. Kodu en ayrıntılı şekilde yeniden yazın, tüm gizli değişkenleri adlandırın
  3. Kodun küçük bir bölümünün doğru çalıştığından emin olmak için birim testleri yazın
  4. Küçük bir kod bölümü daha ekleyin ve herşeyin doğru şekilde çalışmasını sağlayın.

İpuçları

  • Döngü mantığını basitleştirmek için bant sayısını sabit yapmak isteyebilirsiniz
  • Net ve özlü olan N, C ve B gibi iyi adlar verin, bu mantıklı hataları daha kolay görmenize yardımcı olur
  • Büyük kod bölümlerini, her biri daha büyük işlemin kısa ve öz bir adımını uygulayan ve doğru şekilde çalışmasını sağlamak için yazılmış birim sınamaları yapan birkaç yönteme ayırın.

Ben bilmece iyi olduğu sürece, kod bilmeceleri çözme hayranıyım. Dolayısıyla ödül. Aldığına sevindim ve koddaki hataları bulmak için verdiğin cevaplar bir kod çözücünün alabileceği en iyi cevap.
Seth Battin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.