4
$\begingroup$

I am working on a personal project that maps bass notes to colors in real-time. However, I'm encountering some issues with oscillations in my frequency bins.

I visualized my frequency bins to illustrate the issue:

Here is an outline of my signal processing chain:

  1. Capture 2205 samples from my input device at a sample rate of 44.1 kHz (allowing me to identify frequencies down to 20 Hz)
  2. Apply the Hann function to the samples
  3. Calculate frequency bins from 20 Hz to 150 Hz in 1 Hert increments using the Goertzel algorithm

It seems like the oscillations occur at all frequencies, but become more noticeable the lower I go. I assume this is because given some period, the lower the frequency the fewer the cycles captured in that period. I had expected that to result in more leakage, but not necessarily oscillating values. Increasing the sampling period helps, but necessarily increases perceived latency (because I'm using samples further back in time).

Does my assumption about the sampling period seem correct? Are there ways to mitigate this oscillation without increasing my sampling period significantly?

30 Hz tone graphed from real data: enter image description here

$\endgroup$
8
  • 1
    $\begingroup$ I didn’t think about your entire question in detail yet but it jumped out at me that you are using the Goertzel algorithm to compute the DFT for 120 bins- given that many bins wouldn’t the FFT be much simpler and more effficient? (I assume you are facing precision issues with the Goertzel but we needn’t dig into that if you can use the FFT instead). Did I miss something as to why you want to use the Goertzel? I believe the Goertzel would be more efficient for 22 bins or less in your case but a lot more inefficient for 120 bins! $\endgroup$ Feb 23 at 15:19
  • 1
    $\begingroup$ Posting an example graph from a channel with oscillation could be helpful. $\endgroup$
    – TimWescott
    Feb 23 at 16:18
  • 1
    $\begingroup$ I believe that this is a problem of the period of each frequency. Imagine that at some time you capture one period and a fraction of the next at the $30 \textrm{Hz}$ frequency. Inevitably (if I am not completely screwing up here), some windows will have more energy than the previous or the next so you’ll get amplitude fluctuations. Even more, since you apply windowing, just changing the phase of your frequency will probably affect the frame's energy. $\endgroup$
    – ZaellixA
    Feb 23 at 16:30
  • $\begingroup$ @DanBoschen Good question! I had been using FFT up until recently, but it had the same issue. The switch to Goertzel was really just an experiment. $\endgroup$ Feb 23 at 23:18
  • 1
    $\begingroup$ @ZaellixA Yeah - I was afraid of that sort of issue. The oscillations probably become less severe when I provide more samples because the ratio of full to partial cycles improves. $\endgroup$ Feb 23 at 23:19

2 Answers 2

5
$\begingroup$

This is indeed spectral leakage and specifically the dependency of the leakage on phase. Here is an example of 30 Hz sine with with a random phase. These are 50 frames with a randomized phase.

enter image description here

The "flickering" is easy to see and it's also easy to see why it happens. At 2205 sample the window will include 1.5 periods are 30 Hz. For a phase of $\varphi = 0$ the extra half period will be entirely positive so you will see a very significant DC component. For a phase of $\varphi = -\pi/2$ the extra half period will be symmetric, so the DC component will be 0.

The easy fix is to simply make your frames larger. This way the "extra" fractional periods become a much smaller part of the overall signal and you get better frequency resolution as well since the main lobe of the hanning window becomes much narrower.

Below is the same example but with 4 times the frame size

enter image description here

$\endgroup$
2
  • $\begingroup$ That was a fantastic explanation! Thank you. $\endgroup$ Feb 25 at 23:17
  • $\begingroup$ nice animation! $\endgroup$ Mar 1 at 21:25
4
$\begingroup$

The idea that you're looking for here is spectral leakage. As alluded to in the comments, the DFT bins will undergo different amounts of spectral leakage depending on the initial phase of the tone going into the DFT.

The DFT is defined for integer multiples of the fundamental frequency, which is $\frac{f_{s}}{N}$. If your tone frequency is not an integer multiple of the fundamental frequency, you will end up with spectral leakage, which will spread out your spectrum. There's not really a good way to predict how much spectral leakage a given input to the DFT will suffer, but if your tone is not an integer multiple of the fundamental frequency, you will suffer spectral leakage as there will be a discontinuity in the assumed periodicity of the DFT.

Here's a Matlab script to demo this:

fs = 44.1e3;
N = 2205;
f1 = 30;
f2 = 40;

t = (0:N-1)/fs;
c1 = cos(2*pi*f1*t);
c2 = cos(2*pi*f1*t + pi/2);
c3 = cos(2*pi*f2*t);
c4 = cos(2*pi*f2*t + pi/2);

w = ((0:N-1)-N/2)*fs/N;
win = hanning(2205).';

figure;
plot(w,fftshift(abs(fft(c1.*win))));
title('30 Hz Aperiodic No Phase Offset')
xlabel('Frequency (Hz)')
ylabel('FFT Magnitude')
axis([-3000 3000 -100 600])

figure;
plot(w,fftshift(abs(fft(c2.*win))));
title('30 Hz Aperiodic Phase Offset')
xlabel('Frequency (Hz)')
ylabel('FFT Magnitude')
axis([-3000 3000 -100 600])

figure;
plot(w,fftshift(abs(fft(c3.*win))));
title('40 Hz Periodic No Phase Offset')
xlabel('Frequency (Hz)')
ylabel('FFT Magnitude')
axis([-3000 3000 -100 600])

figure;
plot(w,fftshift(abs(fft(c4.*win))));
title('40 Hz Periodic Phase Offset')
xlabel('Frequency (Hz)')
ylabel('FFT Magnitude')
axis([-3000 3000 -100 600])
$\endgroup$
4
  • $\begingroup$ My project will only calculate the color based on the peaks within the range, so the spreading effect of spectral leakage is not necessarily an issue. The problem is magnitudes of the frequency bins oscillate and may cause the peak(s) to alternate, thus causing the hue to alternate in bothersome ways at certain frequencies. So - my goal is to reduce the amount of oscillations, not necessarily reduce the spreading. $\endgroup$ Feb 23 at 23:40
  • $\begingroup$ If I understand you and @ZaellixA correctly, my phase is probably changing between windows because (most) frequencies will not have an integer number of periods in a given window size. The changing phase is resulting my the oscillations I'm observing. Is my understanding correct? If so, what options exist to mitigate the phasing issue? (P.s. Thank you for the Matlab script. I will experiment with it.) $\endgroup$ Feb 23 at 23:55
  • 1
    $\begingroup$ @AlexLarson the phase of the incoming signal is not so much the issue. The issue is that most frequencies won't be an integer multiple of the fundamental frequency, which is the cause of the spectral leakage. The amount and location of the spectral leakage that occurs because of this depends on the initial phase of the incoming tone. Windowing definitely helps reduce spectral leakage. Another idea would be to take the $log_{10}$ of the spectrum. This will reduce variability in the peaks because it compresses the dynamic range. $\endgroup$
    – Baddioes
    Feb 24 at 2:32
  • $\begingroup$ @AlexLarson no problem for the Matlab script! Happy to help! $\endgroup$
    – Baddioes
    Feb 24 at 2:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.