Other things on this site...

Evolutionary sound
Listen to Flat Four Internet Radio
Learn about
The Molecules of HIV
Make Oddmusic!
Make oddmusic!

How to analyse pan position per frequency of your sound files

Someone on the Linux Audio Users list asked how they could analyse a load of FLAC files to work out if it was true for their music collection, that bass frequencies below about 150 Hz (say) tended to be centre-panned. Here's my answer.

First of all, coincidentally I know that Pedro Pestana published a nice analysis of exactly this phenomenon, at the AES 53rd conference recently. He actually looked at hundreds of number-one singles to determine the relationship between panning and frequency in the habits of producers/engineers for popular tracks. The paper isn't open access unfortunately but there you go.

So anyway here's a Python script I just wrote: script to analyse your audio files and plot the distribution of panning per frequency. And here's how it looks when I analyse the excellent Rumour Cubes album:

(Just to stress, this is a simple analysis. It simply looks at the spectral representation of the complete mix, it doesn't infer anything clever about the component parts of the mix.)

See any patterns? The pattern I was looking for is a bit subtle, but it's right down at the bottom below 100 Hz (i.e. 0.1 kHz on the scale): the bass tends to "pinch in" and not get panned around so much as the other stuff.

This analysis of Lotus Flower by Radiohead (by Daniel Jones) shows the effect more clearly.

This is what's generally observed, and widely known in mixing engineer "folklore": pan your bass to the centre, do what you like with the rest. Not everyone agrees on the reasons: some people say it's because the bass can cause the needle to skip out of vinyl records if it's off-centre, some people say it's because we can't really perceive the spatialisation very well at low frequencies, some people say it's just to maximise the energy in the mix. I have no comment on what the reasons might be, but it's certainly folk wisdom for various audio people, and empirically you can test it for yourself by analysing some of your music collection.

NOTE: Code and image updated 2014-02-08, thanks to Daniel Jones (see comments below) for spotting an issue.

Friday 7th February 2014 | sound | Permalink
Name: Daniel Jones
Website: http://www.erase.net/
Email: dan-web art erase dort net
Date: Saturday 8th February 2014 12:16
Fascinating research and a really striking visualisation. I was so interested that I couldn't resist having a play with the code, and have come across a couple of problems which mean the actual result is not as clean-cut.

As an example, I tried feeding it a stereo audio file comprising two channels of precisely the same audio. The ideal output should appear as below:


But instead it produced virtually the same heatmap as on the audio given in your example:


The first problem is that the FFT code isn't doing a proper stereo FFT; numpy.fft.fft does not expect two columns of time series and is doing something slightly strange (though I'm not sure what). The second is that, on my system at least, scikits.audiolab.SndFile does not return two identical channels of audio when the stereo file is read! I attempted a rewrite using the 'wave' package and it works much better. This might be a bug in the sndfile interface (unlikely?) or perhaps it is not proving stereo interleaved data as expected.

I have forked the gist with an updated version of the script:


To test it further, I devised another test audio file with instruments in different frequency ranges panned to the left and right. Here are the three outputs;

Original script: https://dl.dropboxusercontent.com/u/6137498/Temp/Panning/stereoscope-render-orig.png
New script with SndFile: https://dl.dropboxusercontent.com/u/6137498/Temp/Panning/stereoscope-render-new-sf.png
New script with wave: https://dl.dropboxusercontent.com/u/6137498/Temp/Panning/stereoscope-render-new-wave.png

The latter correlates exactly with what I would expect.

Here is an analysis of Lotus Flower by Radiohead:

The results are quite interesting. Panning is minimal at very low frequencies (below 100hz) but quickly becomes much wider.

This is Dream Ache by Lone:


Interestingly, the panning seems to suddenly drop off after 20khz - perhaps the result of some stero panning effects applied in post-production.
Name: Dan S
Date: Saturday 8th February 2014 14:32
Ah! thanks a million for spotting this glitch. I'm puzzled by it because it really does seem to be something wrong in audiolab's handling of stereo. (I'm using scikits.audiolab-0.11.0 on ubuntu.) I didn't spot the issue because the faulty plot confirmed my original expectations...

I've updated my code in the original gist. Note that I prefer to use "sndfile" rather than "wave" here, for two reasons: (1) it handles a range of file types (flac/wav/etc) in one call; (2) I can stream the data rather than loading a file into memory - you might have noticed that the "wave" version in much slower to run.
Name: Daniel Jones
Website: http://www.erase.net/
Email: dan-web art erase dort net
Date: Saturday 8th February 2014 14:46
Yes, I'm totally sold on sndfile for those same reasons, not to mention the fact that wave also requires manual unpacking of the binary data... though will definitely spend some time with it before using it in production, to figure out if there's not a larger issue here. Reassuring that you already use it for lots of mono applications.

Thanks for some great sample code on working with numpy + matplotlib. Really outstanding combination for audio analysis.

Add your comments:

I am a:
Everything is optional - and email addresses will be marmalised to protect you
Creative Commons License
Dan's blog articles may be re-used under the Creative Commons Attribution-Noncommercial-Share Alike 2.5 License. Click the link to see what that means...