简体   繁体   中英

NAudio FFT returns small and equal magnitude values for all frequencies

I'm working on a project with NAudio 1.9 and I want to compute an fft for an entire song, ie split the song in chunks of equal size and compute fft for each chunk. The problem is that NAudio FFT function returns really small and equal values for any freq in the freq spectrum.

I searched for previous related posts but none seemed to help me.

The code that computes FFT using 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);
}

The FrequencySpectrum:

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

for a file that contains a sine wave of 440Hz

expected output: values like 0.5 for freq=440 and 0 for the others

actual output: values like 0.000168153987f for any freq in the spectrum

It seems that I made 4 mistakes:

1) Here I'm asumming that sampling freq is 44100. This was not the reason my code wasn't working, though

return new FrequencySpectrum(timeDomain, 44100);

2) Always make a visual representation of your output data! I must learn this lesson... It seems that for a file containing a 440Hz sine wave I'm getting the right result but...

3) The frequency spectrum is a little shifted from what I was expecting because of this:

int m = (int) Math.Log(timeDomain.Length, 2);
FastFourierTransform.FFT(true, m, timeDomain);

timeDomain is an array of size 44100 becaused that's the value of windowSize (I called the method with windowSize = 44100), but FFT method expects a window size with a value power of 2. I'm saying "Here, NAudio, compute me the fft of this array that has 44100 elements, but take into account only the first 32768". I didn't realize that this was going to have serious implications on the result:

float k = freq / ((float) this.samplingFrequency / this.FftWindowSize);

Here this.FftWindowSize is a property based on the size of the array, not on m . So, after visualizing the result I found out that magnitude of 440Hz freq was actually corresponding to the call:

spectrum[371]

instead of

spectrum[440]

So, my mistake was that the window size of fft ( m ) was not corresponding to the actual length of the array ( FrequencySpectrum.FftWindowSize ).

4) The small values that I was receiving for the magnitudes came from the fact that the audio file on which I was testing my code wasn't recorded with enough gain.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM