Я работаю над проектом с NAudio 1.9 и хочу вычислить fft для всей песни, т.е. разделить песню на фрагменты одинакового размера и вычислить fft для каждого фрагмента. Проблема в том, что функция NAudio FFT возвращает очень маленькие и одинаковые значения для любой частоты в частотном спектре.
Я искал предыдущие связанные сообщения, но ни один из них, похоже, не помог мне.
Код, который вычисляет БПФ с помощью NAudio:
public IList<FrequencySpectrum> Fft(uint windowSize) {
IList<Complex[]> timeDomainChunks = this.SplitInChunks(this.audioContent, windowSize);
return timeDomainChunks.Select(this.ToFrequencySpectrum).ToList();
}
private IList<Complex[]> SplitInChunks(float[] audioContent, uint chunkSize) {
IList<Complex[]> splittedContent = new List<Complex[]>();
for (uint k = 0; k < audioContent.Length; k += chunkSize) {
long size = k + chunkSize < audioContent.Length ? chunkSize : audioContent.Length - k;
Complex[] chunk = new Complex[size];
for (int i = 0; i < chunk.Length; i++) {
//i've tried windowing here but didn't seem to help me
chunk[i].X = audioContent[k + i];
chunk[i].Y = 0;
}
splittedContent.Add(chunk);
}
return splittedContent;
}
private FrequencySpectrum ToFrequencySpectrum(Complex[] timeDomain) {
int m = (int) Math.Log(timeDomain.Length, 2);
//true = forward fft
FastFourierTransform.FFT(true, m, timeDomain);
return new FrequencySpectrum(timeDomain, 44100);
}
Частотный спектр:
public struct FrequencySpectrum {
private readonly Complex[] frequencyDomain;
private readonly uint samplingFrequency;
public FrequencySpectrum(Complex[] frequencyDomain, uint samplingFrequency) {
if (frequencyDomain.Length == 0) {
throw new ArgumentException("Argument value must be greater than 0", nameof(frequencyDomain));
}
if (samplingFrequency == 0) {
throw new ArgumentException("Argument value must be greater than 0", nameof(samplingFrequency));
}
this.frequencyDomain = frequencyDomain;
this.samplingFrequency = samplingFrequency;
}
//returns magnitude for freq
public float this[uint freq] {
get {
if (freq >= this.samplingFrequency) {
throw new IndexOutOfRangeException();
}
//find corresponding bin
float k = freq / ((float) this.samplingFrequency / this.FftWindowSize);
Complex c = this.frequencyDomain[checked((uint) k)];
return (float) Math.Sqrt(c.X * c.X + c.Y * c.Y);
}
}
}
для файла, содержащего синусоиду 440 Гц
ожидаемый результат: такие значения, как 0,5 для частоты = 440 и 0 для остальных
фактический вывод: такие значения, как 0,000168153987f для любой частоты в спектре
Кажется, я сделал 4 ошибки:
1) Здесь я предполагаю, что частота дискретизации равна 44100. Однако это не было причиной того, что мой код не работал.
return new FrequencySpectrum(timeDomain, 44100);
2) Всегда сделать визуальное представление ваших выходных данных! Я должен усвоить этот урок... Кажется, что для файла, содержащего синусоиду 440 Гц, я получаю правильный результат, но...
3) Частотный спектр немного смещен от того, что я ожидал из-за этого:
int m = (int) Math.Log(timeDomain.Length, 2);
FastFourierTransform.FFT(true, m, timeDomain);
timeDomain представляет собой массив размером 44100, потому что это значение windowSize (я вызвал метод с windowSize = 44100), но метод БПФ ожидает размер окна со степенью двойки. Я говорю: «Вот, NAudio, вычисли мне fft этого массива, состоящего из 44100 элементов, но учитываем только первые 32768". Я не понимал, что это будет иметь серьезные последствия для результата:
float k = freq / ((float) this.samplingFrequency / this.FftWindowSize);
Здесь this.FftWindowSize — это свойство, основанное на размере массива, а не на
spectrum[371]
вместо
spectrum[440]
Итак, моя ошибка заключалась в том, что размер окна fft (
4) Небольшие значения, которые я получал для величин, были связаны с тем, что аудиофайл, на котором я тестировал свой код, не был записан с достаточным усилением.