Get magnitude of harmonics with fft?

i try to get the magnitude of the first harmonics of a monophonic signal with juce’ fft. i have 2 fft objects that alternate between each other performing forwardFFT. whenever the index of one of the objects reaches the size samples are being pushed into the corresponsing fft object and i calculate the fundamental frequency from it like this:

inline void calculateFundamentalFrequency(const int& curFFT) {
            auto index = 0.f;
            auto max = 0.f;
            for (auto i = 0; i < m_sizeHalf; ++i)
                if (max < m_fftData[curFFT][i]) {
                    max = m_fftData[curFFT][i];
                    index = i;
                }
            m_fundamentalFreq = m_sampleRateSizeInv * index;

this generally seems to work, because if i put this frequency into an oscillator it produces the correct sound. however when i tried to find out what magnitude values this array has and restricted the output to all magnitudes above .4 so it’s not too much to look at i got this output:

0: 8.05188 :: 1: 2.48275 :: 2: 0.763107 :: 3: 0.536809 :: 4: 0.537238 :: 5: 0.400128 :: 6: 0.687796 :: 7: 1.10533 :: 8: 1.16325 :: 9: 1.54366
10: 1.83967 :: 11: 1.23882 :: 13: 0.621443 :: 15: 0.56024 :: 16: 0.526263 :: 17: 0.505439
20: 1.11583 :: 21: 0.55952 :: 23: 0.683966 :: 24: 1.49616 :: 25: 1.75451 :: 26: 2.87058 :: 27: 5.33952 :: 28: 14.2842 :: 29: 117.509
30: 213.158 :: 31: 38.1122 :: 32: 10.3408 :: 33: 4.13143 :: 34: 2.40158 :: 35: 1.63776 :: 36: 1.44324 :: 37: 0.475217 :: 38: 0.918497 :: 39: 0.437185
40: 0.6701 :: 58: 0.442696 :: 59: 13.4757 :: 60: 14.2869 :: 62: 0.830525 :: 89: 1.71559 :: 90: 1.41736 :: 118: 0.402302 :: 119: 1.84722 :: 120: 0.936844
149: 0.558088 :: 357: 0.585806 :: 358: 0.418608 :: 391: 0.437874 :: 392: 1.30164 :: 393: 1.04631 :: 395: 0.573601 :: 417: 0.907743 :: 418: 0.414227 :: 446: 0.469081
447: 1.07932 :: 476: 0.61782 :: 477: 0.636135 :: 754: 0.407764 :: 1173: 0.409816 :: 1192: 0.442336 :: 

ok, so according to this list my fundamental frequency must be at the highest mag. idk which it is but if you look at index no 29 for example you see a magnitude of 117. first of all this is confusing me a lot because i expected normalized values here. but that’s not all. i then proceeded to make a function that returns the index of the bin that holds the magnitude of some frequency and it works like this:

freq * (size / 2) / sampleRate

when i feed my fundamental frequency into this it actually gives me the index 15 with magnitudes around .5 or .6, which is reasonable, because my input signal is almost normalized. but this raises multiple questions… why was this index chosen by my method even though there are also far higher numbers in the array? so why does my fundamentalFrequency-method even work at all? and if it really works: what the hell are those other values doing there? :smiley:

edit: important thing to note: if it’s not possible to get normalized magnitude values that’s fine. i’m only interested in the ratio between the harmonics in the end. i just want to know what’s going on there in general

1 Like

You usually have to divide the complex numbers you get from a FFT by the fftSize to get normalized values.
Regarding the wrong index: did you calculate the abs from the complex numbers before max-picking? Getting index 15 is strange if there’s an index with a way higher magnitude.

that’s good. because it means that it totally makes sense to get such high numbers there.

i didn’t abs any values in this whole process. the magnitude as well as frequency values seem all to be positive, but maybe i’m missing something there… i don’t have a good intuition for complex numbers yet tbh.

i find this situation very confusing. on the one hand i managed to come up with this method that gets a max magnitude from this array and it sonically works. on the other hand its DBG-output didn’t look like i expected.

correct me if i’m wrong but i thought FFT works like that: the real part, the magnitude, is the first half of the array, while the imaginary part, the phase, is the other half. so in order to get the right index for some frequency i took the aspect ratio between the amount of bins (half the m_fftData-array) and the sampleRate and multiplied that with the frequency

Not correct. The magnitude is the absolute value of the complex number sqrt(realˆ2 + imagˆ2) while the phase is the angle of the complex number atan2(imag, real). Both available for std::complex as std::abs and std::arg.

2 Likes

ok good to know. but i still don’t understand how that could be the problem with my implementation. i mean the fft object makes these calculations that you just mentioned when you’re performing forwardFFT, doesn’t it? and in the end it just puts the real numbers into the float array with the first half being magnitude values and the 2nd half being phase values and all of them are positive already at that point… but thanks for the explanation so far. i totally wanna understand this whole process at some point. apparently this point has to be soon with all my fft ideas^^

now i tried to output all fft magnitudes again but this time devide by half the fftsize. by the way the code i use to do that looks like that and comes directly after the fundamental frequency has been calculated:

            static int timer = 0;
            static bool hitit = true;
            ++timer;
            if (timer > 300) {
                timer = 0;
                if (hitit) {
                    hitit = false;
                    String test = "test: ";
                    for (auto a = 0; a < m_sizeHalf; ++a) {
                        auto mag = m_fftData[curFFT][a] / m_sizeHalf;
                        //if (mag > .4f)
                            test += String(a) + String(": ") + String(mag) + " :: ";
                    }   
                    DBG(test);
                }
            }

if(mag > .4) is now commented out because i get literally no value with that, because now the whole array looks like this: https://pastebin.com/u2d46nLC

only super small numbers and none of that indicates that there is a signal with strong harmonic peaks. so this is not exactly what i expected to happen again

No, as @pflugshaupt already explained: real part is not magnitude, it’s the real part of the complex pointer, and the imag part is not phase as it is the imag part of the complex pointer :slight_smile:
You have to reinterpret the inOutData pointer as a pointer to std::complex<float> data.
The values in that float array have this layout:
r0 i0 r1 i1 r2 i2 ... so each pair is one complex number.

The thing about first half vs second half is that the first half contains positive frequencies, that’s all you need when you perform a FFT on real data. The second half are the negative frequencies, which you would need with non-real data, with real data they are just the complex conjugated numbers of the positive frequencies.

It’s strange that your raw FFT output has only positive values all the time, can you show us the code where you perform the FFT and what you do in between with the data?

Edit: Wait, did you use performFrequencyOnlyForwardTransform()? Then yes, JUCE already calculates the magnitude for you, the second half of the array should be empty then. Sorry for the long talk then :wink:

thanks for the long talk nevertheless. i wanna learn something about fft anyway. :slight_smile:

this is the method that gets the samples from processBlock:

void pushSampleIntoFifo(const float& sample) {
            for (auto i = 0; i < m_fft.size(); ++i) {
                if (m_fifoIdx[i] == m_size) {
                    std::fill(std::next(m_fftData[i].begin(), m_size), m_fftData[i].end(), 0.f); // this clears shit
                    std::copy(m_fifo[i].begin(), m_fifo[i].end(), m_fftData[i].begin()); // this copies shit (apparently^^)
                    m_fft[i]->performFrequencyOnlyForwardTransform(m_fftData[i].data());
                    calculateFundamentalFrequency(i);
                    calculateMagnitudeOfHarmonics(i);
                    m_fifoIdx[i] = 0;
                }

                float windowAmp;
                switch (m_windowType) {
                case WindowType::Linear:
                    windowAmp = m_fifoIdx[i] < m_sizeHalf ?
                        float(2 * m_fifoIdx[i]) * m_sizeMaxInv :
                        2.f - float(2 * m_fifoIdx[i]) * m_sizeMaxInv;
                    break;
                case WindowType::Sine:
                    windowAmp = std::sin(float(m_fifoIdx[i]) * m_sizeMaxInvPi);
                    break;
                default:
                    // No Windowing
                    windowAmp = 1.f;
                    break;
                }
                m_fifo[i][m_fifoIdx[i]] = sample * windowAmp;
                ++m_fifoIdx[i];
            }
        }

in processBlock i summed the samples to mid-only before sending it to the fft so it’s just a mono fft.

that’s basically the code from the fft tutorial. the only difference is that there is some windowing and a method that gets the fundamentalFrequency and magnitude of harmonics… well at least that’s the plan :slight_smile: