NAudio fft результат дает интенсивность на всех частотах C#



У меня есть рабочая реализация записи WASAPI loopback NAudio и FFT данных.
Большинство данных, которые я получаю, такие же, как и должны быть, но время от времени (с интервалом от 10 секунд до нескольких минут) они показывают амплитуду почти на всех частотах.



Изображение ошибки



В основном картина катится справа налево со временем и частотами, идущими по логарифмической шкале от самых низких частот внизу. Линии - это ошибки. Насколько я могу судить, это не так. должен был быть там.

Я получаю звуковой буфер и отправляю образцы в агрегатор (применяет окно Хэмминга), который реализует NAudio FFT. Я проверил данные (результат БПФ), прежде чем изменить его каким-либо образом (изображение не является результатом необработанного вывода БПФ, но масштабируется десибелем), подтверждая, что результат БПФ дает эти строки. Я также мог бы указать, что изображение модифицировано с помощью Локбитов, поэтому я подумал, что у меня что-то не так с логикой, но именно поэтому я проверил выходные данные FFT что показывает ту же проблему.



Ну, я могу ошибаться, и проблема может быть где-то, где я сказал, что это не так, но на самом деле кажется, что это происходит из БПФ или буферных данных (сами данные или агрегация выборок). Почему-то я сомневаюсь, что сам буфер поврежден подобным образом.



Если у кого-нибудь есть хоть малейшее представление о том, что могло бы вызвать это, я буду очень признателен!



Обновить



Поэтому я решил нарисовать весь диапазон результатов FFT, а не половину его. Это было заметно что-то странное. Я не уверен в FFT, но я думал, что преобразование Фурье должно дать результат, который отражается примерно посередине. Здесь, конечно, дело обстоит иначе.



Изображение имеет линейный масштаб, поэтому точная середина изображения является средней точкой результата БПФ. Низ-первый, а верх-последний.



wholefft



Я играл синусоидальную волну 10 кГц, которая дает две горизонтальные линии, но верхняя часть находится за пределами меня. Это также кажется, что линии зеркально отражаются в нижней части рисунка, так что мне это тоже кажется странным.

Обновление 2



Поэтому я увеличил размер БПФ с 4096 до 8192 и попробовал снова. Это выход с моей возней с синусоидальной частотой.

picture3



Казалось бы, результат повторяется дважды. Один раз в середине, а затем снова на верхней и нижней половинках. И огромные линии теперь исчезли.. И казалось бы, линии появляются только на экране. теперь нижняя половина.



После некоторого дальнейшего тестирования с различными длинами FFT кажется, что линии полностью случайны в этом счете.



Обновление 3



Я провел некоторые испытания со многими вещами. Последнее, что я добавил, Было наложение выборок, так что я повторно использую последнюю половину массива выборок в начале следующего БПФ. На окнах Хэмминга и Ханна это дает мне огромные интенсивности (совсем как на второй картинке, которую я опубликовал), но не с BlackmannHarris. Отключение перекрытия устраняет самые большие ошибки в каждой функции окна. Меньшие ошибки, как на верхнем рисунке, все еще остаются даже с окном BH. Я до сих пор не знаю, почему появляются эти линии.



Моя текущая форма позволяет контролировать, какую функцию окна использовать (из трех ранее упомянутых), перекрывая (ВКЛ/ВЫКЛ) и несколько различных вариантов рисования. Это позволяет мне сравнить все затрагивающие стороны эффекты при изменении.



Я буду исследовать дальше (я вполне конечно, я сделал ошибку в какой-то момент), но хорошие предложения более чем приветствуются!

884   1  

1 ответ:

Проблема заключалась в том, как я обрабатывал массивы данных. Теперь работает как заклинание.

Код (убрал лишнее и, возможно, добавил ошибки):

// Other inputs are also usable. Just look through the NAudio library.
private IWaveIn waveIn; 
private static int fftLength = 8192; // NAudio fft wants powers of two!

// There might be a sample aggregator in NAudio somewhere but I made a variation for my needs
private SampleAggregator sampleAggregator = new SampleAggregator(fftLength);

public Main()
{
    sampleAggregator.FftCalculated += new EventHandler<FftEventArgs>(FftCalculated);
    sampleAggregator.PerformFFT = true;

    // Here you decide what you want to use as the waveIn.
    // There are many options in NAudio and you can use other streams/files.
    // Note that the code varies for each different source.
    waveIn = new WasapiLoopbackCapture(); 

    waveIn.DataAvailable += OnDataAvailable;

    waveIn.StartRecording();
}

void OnDataAvailable(object sender, WaveInEventArgs e)
{
    if (this.InvokeRequired)
    {
        this.BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e);
    }
    else
    {
        byte[] buffer = e.Buffer;
        int bytesRecorded = e.BytesRecorded;
        int bufferIncrement = waveIn.WaveFormat.BlockAlign;

        for (int index = 0; index < bytesRecorded; index += bufferIncrement)
        {
            float sample32 = BitConverter.ToSingle(buffer, index);
            sampleAggregator.Add(sample32);
        }
    }
}

void FftCalculated(object sender, FftEventArgs e)
{
    // Do something with e.result!
}

И класс агрегатора выборки:

using NAudio.Dsp; // The Complex and FFT are here!

class SampleAggregator
{
    // FFT
    public event EventHandler<FftEventArgs> FftCalculated;
    public bool PerformFFT { get; set; }

    // This Complex is NAudio's own! 
    private Complex[] fftBuffer;
    private FftEventArgs fftArgs;
    private int fftPos;
    private int fftLength;
    private int m;

    public SampleAggregator(int fftLength)
    {
        if (!IsPowerOfTwo(fftLength))
        {
            throw new ArgumentException("FFT Length must be a power of two");
        }
        this.m = (int)Math.Log(fftLength, 2.0);
        this.fftLength = fftLength;
        this.fftBuffer = new Complex[fftLength];
        this.fftArgs = new FftEventArgs(fftBuffer);
    }

    bool IsPowerOfTwo(int x)
    {
        return (x & (x - 1)) == 0;
    }

    public void Add(float value)
    {
        if (PerformFFT && FftCalculated != null)
        {
            // Remember the window function! There are many others as well.
            fftBuffer[fftPos].X = (float)(value * FastFourierTransform.HammingWindow(fftPos, fftLength));
            fftBuffer[fftPos].Y = 0; // This is always zero with audio.
            fftPos++;
            if (fftPos >= fftLength)
            {
                fftPos = 0;
                FastFourierTransform.FFT(true, m, fftBuffer);
                FftCalculated(this, fftArgs);
            }
        }
    }
}

public class FftEventArgs : EventArgs
{
    [DebuggerStepThrough]
    public FftEventArgs(Complex[] result)
    {
        this.Result = result;
    }
    public Complex[] Result { get; private set; }
}

И это все, что я думаю. Хотя, возможно, я что-то упустил. Надеюсь, это поможет!

Comments

    Ничего не найдено.