NAudio fft результат дает интенсивность на всех частотах C#
У меня есть рабочая реализация записи WASAPI loopback NAudio и FFT данных. Большинство данных, которые я получаю, такие же, как и должны быть, но время от времени (с интервалом от 10 секунд до нескольких минут) они показывают амплитуду почти на всех частотах.
В основном картина катится справа налево со временем и частотами, идущими по логарифмической шкале от самых низких частот внизу. Линии - это ошибки. Насколько я могу судить, это не так. должен был быть там.Я получаю звуковой буфер и отправляю образцы в агрегатор (применяет окно Хэмминга), который реализует NAudio FFT. Я проверил данные (результат БПФ), прежде чем изменить его каким-либо образом (изображение не из необработанного вывода БПФ, а в масштабе десибеля), подтвердив, что результат БПФ дает эти строки. Я также мог бы указать, что изображение модифицировано с помощью Локбитов, поэтому я подумал, что у меня что-то не так с логикой, но именно поэтому я проверил выходные данные FFT что показывает ту же проблему.
Ну, я могу ошибаться, и проблема может быть где-то, где я сказал, что это не так, но на самом деле кажется, что это происходит из БПФ или буферных данных (сами данные или агрегация выборок). Почему-то я сомневаюсь, что сам буфер поврежден подобным образом.
Если у кого-нибудь есть какие-либо идеи, что может вызвать это, я буду очень признателен!
Обновить
Поэтому я решил нарисовать весь диапазон результатов FFT, а не половину его. Это было заметно что-то странное. Я не уверен в FFT, но я думал, что преобразование Фурье должно дать результат, который отражается примерно посередине. Здесь, конечно, дело обстоит иначе.
Изображение имеет линейный масштаб, поэтому точная середина изображения является средней точкой результата БПФ. Низ-первый, а верх-последний.
Я играл синусоидальную волну 10 кГц, которая дает две горизонтальные линии, но верхняя часть находится за пределами меня. Это также кажется, что линии зеркально отражаются вокруг нижней четверти изображения, так что мне это тоже кажется странным.Обновление 2
Поэтому я увеличил размер БПФ с 4096 до 8192 и попробовал снова. Это выход с моей возней с синусоидальной частотой.Казалось бы, результат повторяется дважды. Один раз в середине, а затем снова на верхней и нижней половинках. И огромные линии теперь исчезли.. И казалось бы, линии появляются только на экране. теперь нижняя половина.
После некоторого дальнейшего тестирования с различными длинами FFT кажется, что линии полностью случайны в этом счете.
Обновление 3
Я провел некоторые испытания со многими вещами. Последнее, что я добавил, Было наложение выборок, так что я повторно использую последнюю половину массива выборок в начале следующего БПФ. На окнах Хэмминга и Ханна он дает мне огромные интенсивности (совсем как на второй картинке, которую я опубликовал), но не с BlackmannHarris. Отключение перекрытия устраняет самые большие ошибки в каждой функции окна. Меньшие ошибки, как на верхнем рисунке, все еще остаются даже с окном BH. Я до сих пор не знаю, почему появляются эти линии.
Моя текущая форма позволяет контролировать, какую функцию окна использовать (из трех ранее упомянутых), перекрывая (ВКЛ/ВЫКЛ) и несколько различных вариантов рисования. Это позволяет мне сравнить все влияющие стороны эффектов при изменении.
Я буду исследовать дальше (я вполне конечно, я сделал ошибку в какой-то момент), но хорошие предложения более чем приветствуются!
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; } }
И это все, что я думаю. Хотя, возможно, я что-то упустил. Надеюсь, это поможет!