C#: Как правильно использовать БПФ для определения сходства формы сигнала?

У меня есть 2 стерео формы ADPCM. 1-й - исходный сигнал, а 2-й - 10-секундный обрез исходного сигнала.
Мне нужно найти метку времени в миллисекундах, где второй клип начинается в первом клипе.

Я провел небольшое исследование и выяснил, что FFT - это путь. Однако я очень плохо знаком с DSP, поэтому я надеюсь, что кто-то может указать мне правильное направление.

я использую NAudio 1.7.3 преобразовать сигнал в PCM и AForge 2.2.5 выполнить БПФ.

Вот что я сделал до сих пор:

// ADPCM -> PCM -> Wave32
// 44.1khz 16bits 2ch
using (var song = new WaveFileReader(songFile))
using (var songuncompressed = WaveFormatConversionStream.CreatePcmStream(song))
using (var songwav = new WaveChannel32(songuncompressed))
using (var preview = new WaveFileReader(previewFile))
using (var previewuncompressed = WaveFormatConversionStream.CreatePcmStream(preview))
using (var prevwav = new WaveChannel32(previewuncompressed))
{
    // Read files into buffer
    var prebuffer = new byte[prevwav.Length];
    var songbuffer = new byte[songwav.Length];
    prevwav.Read(prebuffer, 0, (int)prevwav.Length);
    songwav.Read(songbuffer, 0, (int)songwav.Length);

    // byte[] -> float[]
    float[] prefloats = new float[prebuffer.Length / sizeof(float)];
    float[] songfloats = new float[songbuffer.Length / sizeof(float)];
    Buffer.BlockCopy(prebuffer, 0, prefloats, 0, prebuffer.Length);
    Buffer.BlockCopy(songbuffer, 0, songfloats, 0, songbuffer.Length);

    int FFTSize = 1024;
    int increment = prefloats.Length / FFTSize; // Not sure if this is correct?
    double step = songwav.Length / songwav.TotalTime.TotalMilliseconds / 4; //float per millisecond
    double smallest = double.MaxValue;
    int millisecond = 0;

    var presample = new Complex[FFTSize];
    var songsample = new Complex[FFTSize];

    // Preview data
    for (int a = 0; a < FFTSize; a++)
        presample[a] = new Complex(prefloats[a * increment], 0);

    FourierTransform.FFT(presample, FourierTransform.Direction.Forward);

    // Sliding fft (1ms per step)
    for (double pos=0; pos < (double)songfloats.Length; pos += step)
    {
        int offset = (int)pos;

        // Song data
        for (int b = 0; b < FFTSize; b++)
        {
            // Check for overflow
            // Fill remaining samples with 0 if overflow
            int idx = Math.Min(b * increment + offset, songfloats.Length);

            if(idx != songfloats.Length)
                songsample[b] = new Complex(songfloats[idx], 0);
            else
                songsample[b] = new Complex(0, 0);
        }

        FourierTransform.FFT(songsample, FourierTransform.Direction.Forward);

        // Simple comparison through magnitude difference
        // Not sure if it is the right way to compare results
        double similarity = 0;
        for(int i =0; i < FFTSize; i++)
        {
            similarity += Math.Abs(presample[i].Magnitude - songsample[i].Magnitude);
        }

        if(similarity < smallest)
        {
            smallest = similarity;
            int bytes = (int)pos * 4; // bytes position
            // bytes / bytesPerMS for actual millisecond
            millisecond = bytes / (int)(songwav.Length / songwav.TotalTime.TotalMilliseconds); 
        }
    }

    // Result is +- 200ms
    return millisecond;
}

3 вещи, в которых я не очень уверен:

  1. Это правильный способ выборки данных?
  2. Какой правильный метод для сравнения БПФ по сходству?
  3. Как FFTSize влияет на результаты?

0 ответов

Другие вопросы по тегам