libsndfile/sndfile-tools

sndfile-spectrogram produces poor spectrograms for some inputs.

Closed this issue · 13 comments

For instance this pair of commands produces a spectrogram with a bunch of rubbish in it.

$ sndfile-generate-chirp -amp 1.0 -linear -from 22000 -to 26000 96000 10 chirp96.wav
Start frequency :  22000.0 Hz (1.439897 rad/sec)
End   frequency :  26000.0 Hz (1.701696 rad/sec)
$ sndfile-spectrogram chirp96.wav 800 600 chirp96.png

I've tried changing the window function in case it was related to that, both with success

Kaiser window:
chirp96-kaiser
Nuttall window (enabled from oxbow code in the source):
chirp96-nuttall
Hanning window from WikiPedia { data [k] = 0.5 * (1.0 - cos(2.0 * M_PI * k / (datalen - 1))); }
chirp96-hanning
It looks from these that the Nuttall window gives better frequency-domain separation than Hanning, though Kaiser is even tighter that that.

Is this a property of the Kaiser window itself, to resonate at certain frequencies?
Zooming in on one of the frequency bands, it looks chaotic.
24025-24075:
chirp24025-24075-kaiser
24050-24056:
chirp24050-24056-kaiser

erikd commented

Thanks. Applied.

This issue is still with us unfortunately.
Analysing http://martinwguy.co.uk/test/50Hz.wav
sndfile-spectrogram --no-border --dyn-range=100 50Hz.wav 480 480 50Hz-kaiser-480-480.png
50hz-kaiser-480-480
With --nuttall:
50hz-nuttall-480-480
With --hanning:
50hz-hanning-480-480

Note: the above interpolate from spec_len==2880 to heignt==480. However, the results are just as bad at size 480x2880 so it doesn't look like the interpolator.

I'd like to get to the bottom of this. Erik, can you reopen this issue please?

erikd commented

The is an extremely artificial signal. I think what you are seeing is the effect of the transient at the onset of sine tone.

I don't think it's the onset (which is brusque, yes) because the erroneous vertical lines only appear when the FFT window only covers an area with pure 50Hz sine tone. It's only when a constant 50Hz tone is the only signal in town that the effect occurs, the same as with the "chirp" test piece. A gut felling says "integer overflow", but it can't be because we are in floating point world!

erikd commented

For the 50Hz signal, I wonder is the window length is too small to capture a full cycle of the sine wave.

erikd commented

A good experiment would be to do exactly this test for something around 200Hz> If there is no problem there, then reduce the frequency until it becomes a problem.

That might give us an idea what speclen should actually be.

Thanks for the input.
Hypothesis: For the 50Hz signal, the window length is too small to capture a full cycle of the sine wave.
The test piece is at 44100 samples per second so a 50th of a second is 44100/50=882 samples. The above tests select spec_len==2880 so it has 3 cycles in its window.
Experiment: run the test on a 200Hz tone. It uses speclen=2880 again, so 1 cycle is 220 samples, 2880/220==13 cycles in-window
sndfile-spectrogram --no-border --dyn-range=100 200Hz.wav 480 480 200Hz-kaiser-480-480.png
200hz-kaiser-480-480
Observation: The defect persists so it's not that it has only part of a cycle in-window.

Observation: It's very sensitive to tiny changes in --dyn-range. For the above tests, --dyn-range=100. Without --dyn-range the artifacts are replaced by a different kind of noise:
200hz-kaiser-480-200-nodyn
and changing dyn_range but a tiny amount radically changes the noise pattern:
--dyn-range=102:
200hz-kaiser-480-200-dyn102
Hypothesis: It's a defect in the colour mapping code, not in the FFT output.
Experiment: Try the test with --gray-scale
sndfile-spectrogram --no-border --dyn-range=100 --gray-scale 200Hz.wav 480 200 200Hz-kaiser-480-200-gray.png
200hz-kaiser-480-200-gray
Adding --gray-scale to the original "chirp96" testpiece also gives clean output.

Hypothesis confirmed! Something's going screwy in the colour-mapping code.

erikd commented

Ok, I'm going to break the get_colour_map_value function into a separate file and write some tests for it.

erikd commented

Please test commit:

commit 9ca586748b2029b86a9b225178153953e0698f1e
Author: Erik de Castro Lopo <erikd@mega-nerd.com>
Date:   Sat Dec 12 20:11:10 2015 +1100

src/spectrogram.c: Fix colour map calculation

I think that may have fixed it.

Yes, that fixes all the above testcases.

erikd commented

Wonderful!