# Aquila DSP C++ Library - Wave file FFT analog frequency off by factor of 4?

I'm working with the Aquila C++ DSP library. I'm computing the FFT of a wave file (16 bit depth, single channel, 44100 sample rate). I am using a window size of 16384 to calculate the FFT spectrum.

I'm having an issue converting the the digital frequency (0/N, 1/N, ... N/N) to the corresponding analog frequency. When I apply the sample rate multiplier S,

f = S*i/N; // Convert from digital frequency to analog frequency

I get analog frequencies that are 4 times greater than what I expect.

For example, I read in a pianos A1 note. I expect this to be ~55 Hz. Instead I'm getting ~220 Hz. Refer to the photo below.

Now I'm first wondering if I'm scaling my the wrong number. However, I'm pretty sure it's correct. So now I'm curious if somehow the time domain data I'm reading is compressed by a factor of 4, such that the frequencies appear to be 4 times greater. Does anyone know if there is a common .wav file 'gotch-ya' when reading from them?

I've posted the relevant part of my code if anyone is interested.

/* sample window */
int window_size = 16384;

/* Calculate the FFT */
std::shared_ptr<Aquila::Fft> p_fft_interface = Aquila::FftFactory::getFft(window_size);  // This returns a shared pointer to an FFT calculation object.
auto spectrum = p_fft_interface->fft(wave_object.toArray());

QVector<double> x(window_size);
QVector<double> y(window_size);

/* Prepare to plot & convert to analog frequency */
double max_value = 0;
for(int i = 0; i < window_size; i++)
{
x[i] = i*(sample_freq/window_size);
y[i] = abs(spectrum[i]);

if(abs(spectrum[i]) > max_value)
{
max_value = abs(spectrum[i]);
}
}

• I believe you should try using the FFT with some known signal first, such as a steady-state sinewave of known frequency. After you verify correct results and be sure of the use of the API you could proceed with more complex signals. Mar 30, 2022 at 16:57

An alternative to other answers (that sugguest you're passing real-values to complex-fft).

One common mistake that results in exactly this error is an incorrect sampling rate parameter. It would sound like slow-motion if this is the case.

If the .wav file was actually sampled at 1/4 of the rate entered, your frequency spectrum would reflect this as well. It's possible your file was sampled at 11025 Hz. Try and verify the sampling rate both on your OS and within Aquila.

You didn't say how to obtained the signal, but another possibility is your mic filters out low frequencies, try putting another wav file in the function and see if you find any power below the 100 Hz range. This will confirm it's not just the signal.

Not familiar with Aquila in particular, and I don't see how you're mapping the output spectrum samples to an X-axis that ends at 800 Hz, but remember that a real-only FFT mirrors its output data in the second half of the buffer. You may need to run your loop from 0 to window_size/2 if Aquila's FFT is implemented the usual way.

That could account for a factor of 2x.

• Even if this is the case, the first half should be OK in OP's implementation, since the scaling factor shouldn't change whether you pass real or complex valued signals. In all cases the bin frequency should be $i \frac{windowSize}{f_{s}}$ with $i$ denoting the bin index ranging from $0$ to the length of the frame/window $-1$ and $f_{s}$ the sampling frequency. Mar 30, 2022 at 16:48

Further to @user572's answer, the other 2x could be coming from the fact you are passing a real array to a complex FFT: p_fft_interface->fft(...). Check the documentation for p_fft_interface->fft to make sure it's not treating your samples as pairs of real/imaginary components rather than just real.

• If this would be the case, I believe it would be visible in the spectrum as abruptly changing values since the magnitude of real and purely imaginary numbers wouldn't be smooth (this is just assumption here but I don't think there's any specific relation showing that real and imaginary components of a signal's Fourier Transform are similar in magnitude). I may be wrong though or missing something else here... Mar 30, 2022 at 16:55