Linear spectrogram |
Unfortunately this is currently not supported in SoX, so I modified the spectrogram module to add this feature. This I noticed that plotting charts from 1Hz to the nyquist frequency meant that a lot of screen space was wasted in the lower 1 - 50Hz range which is not the most interesting for most audio files. So I also added a switch to set the lower and upper frequencies of the chart (this actually took considerably more work than the plotting to the log scale!).
Log axis spectrogram. Lower frequency details are far more visible. |
Summary of changes:
- Implement -L which plots the spectrogram on a log10 frequency axis. By default from 1Hz to nyquist frequency.
- Implement -R <low_freq>:<high_freq> : restrict spectrogram frequencies. I also updated the linear scale code to honor this switch.
Example:
sox mymusic.mp3 -n spectrogram -L -R 50:8k
Known problems and observations:
- At lower frequencies (1 - 100Hz) each spectrum bin in q->dBfs[] array occupies several pixel rows: so it looks blocky. A lesser problem: at the top some frequency bins will be ignored because there will be more than one bin for each pixel row. I could fix the blockyness by getting a more detailed spectrum.
- If lower and upper frequencies are in the same decade then there are no y axis tick labels. Hopefully will have a fix for that soon.
- When compiling I'm getting this warning when using log10f() and powf() : "warning: passing argument 1 of 'log10f' as 'float' rather than 'double' due to prototype". According to the man pages for those function they should accept float args! I see some discussion on this relating to compiler switches: but I don't want to go changing anything there.
- I created two new functions to parse the -R frequency range switch to cut down on code duplication.
parse_range (const char *s, int *a, int *b)
parse_num_with_suffix (const char *s, int *a)
I'm not familiar with the sox code base, so I'm not sure if similar functions already exist (I couldn't find any). I'm also concerned I'm making a temporary change to a string in parse_range when the type is const char *. I need to brush up on my C :)
Footnotes:
[1] https://github.com/jdesbonnet/joe-desbonnet-blog/tree/master/projects/sox-log-spectrogram
[2] http://sox.sourceforge.net/
10 comments:
Great article! I followed your instructions. Got the source, replaced your file, compiled and installed. but nothing changed.
Is it because I had SoX already installed (by apt-get) and didn't install it. I thought Could this be why it's not working?
Sorry for the delay in replying: yes, if you already have it installed, you'll probably need to have an explicit path to the new binary when invoking sox (I can't remember exactly where the binary is
but your command line might like something like this
/home/jbloggs/my-modified-sox/bin/sox ...sox args...
Log graph is stretched out on bottom. So the frequencies are not scaled in log, but instead it is just the image stretched like log scale, right?
Are there any more updates on this? I'd like to be able to trim visualizations to view certain frequencies, but not channge anything with band-pass filters or sampling rates. This seems like the only thing out there for sox that accomplishes this, but I haven't been able to get it to work.
Fab stuff! I've merged it into https://codeberg.org/sox_ng/sox_ng and it should be released later this year. To fix the blockiness, you need to interpolate between the amplitude spectrum results, and you should be averaging the higher frequency results that fall within that pixel or (better) according to a window function over twice the height of the pixel. Log window functions, gak! I do both of the former in https://codeberg.org/martinwguy/spettro if you'd like to rip code from there. Blessings -M
Fab stuff! I've merged it into https://codeberg.org/sox_ng/sox_ng and it should be released later this year. For the blockiness you need to interpolate between spectral results for the lower frequencies and at higher frequencies you should average the results that fall within the height of each pixel or (better) average twice tthe pixel's height according to a window function. I've done the forner in https://codeberg.org/martinwguy/spettro if you'd like some code to do this. Blessings -M
Fab stuff! I've merged it into https://codeberg.org/sox_ng/sox_ng and it should be released later this year. For the blockiness you need to interpolate between spectral results for the lower frequencies and at higher frequencies you should average the results that fall within the height of each pixel or (better) average twice tthe pixel's height according to a window function. I've done the forner in https://codeberg.org/martinwguy/spettro if you'd like some code to do this. Blessings -M
I have included this in the first minor release candidate of sox_ng
https://codeberg.org/sox_ng/sox_ng/releases/tag/sox_ng-14.5.0-rc1
This has been included in sox_ng-14.5.0-rc1 Please test
codeberg.org/sox_ng/sox_ng/releases
This has been included in sox_ng-14.5.0-rc1
Post a Comment