View RSS Feed

Carnage

PyDj: a project to write a better DJ in python than work in local clubs.

Rate this Entry
A few recent nights out have been ruined by DJ's in clubs that fail really badly at beat matching tracks - it really grates when a new track is mixed in a quater of a beat early/late.

I was convinced that this wasn't really a hard thing to do - with a bit of practice i was able to match stuff up however, being a programmer came to the better conclusion that it shouldn't be too hard to write a program to do it automatically.

Dispite PHP being my usual language of choice, it lacks the speed and abilities to handle media well, so my choice for this project was python. Python hgas the module Pymedia which provides some audio codec's and functions for playing sound. It unfortunatly lacks most of the functions i'll eventually need however does have one useful class. SpectrAnaliser.

Beats are usually, very low notes; In order to match the accuratly, i'll have to filter out all high and middle notes, leaving just the beats. Due to the shockingly poor documentation for the spectranaliser class my code for this ended up very messy with debugging stuff all over the place so i'm not going to post it here. Instead i'm going to explain how the class works.

Code:
analyzer= sound.SpectrAnalyzer( CHANNELS, SAMPLES, NUM_FREQS )
That line is used to initialise the class; it has three parameters (not two as the docs suggest)
  • CHANNELS: the number of Channels in your music track, the documentation suggests that this may only be one and i've not tested it with more, however its possible it'll take 2.
  • SAMPLES: this one confused me a bit, but it seems to be the number of samples it averages when analising. The minimum value for this is 32 , in my code i used 512 which is close to the maximum.
  • NUM_FREQS: i have no idea why this parameter is used for the initalisation (see later) however i've been using it set to 256 - it determines the number of frequencies that the track is split into.


Once you've initialised the class, it has two methods asBands and asFrequencies.

Code:
analyzer.asBands(BANDS,SOUND_DATA)
  • BANDS: the number of bands to seperate the sound into, i used 3 here to get high, mid and low you can use more however for my project 3 appeared to be sufficient.
  • SOUND_DATA: the data to analise; it should have first been decoded by a sutiable pymedia decoder.


the return from this function will be a list of lists of tuples and can be confusing as to what all the data represents. From my experiance, the tuple contains two values, the first appears to be worthless, the second contains a much more useful value.

The inner lists are lists of bands; there will be as meny items in the list as the parameter BANDS.

The outer list is a collection of these lists of bands as they differ over time.

Code:
analyzer.asFrequencies(SOUND_DATA)
The differance between this and the above, is instead of giving you a limited number of bands, it gives you the data split into the number of frequencies you specified in the constructor.

Getting somethine useful from it...

For some reason, when using asBands, the highest level band seemed to give the lowest frequencies. but anyway to get something i could use i did the following list comprehension.

Code:
BANDS = 3
y = analiser.asBands(BANDS,sounddata)
z = [q[<band>][1] for q in y]
where <band> should be replaced by the number of the band you are interested in. To get the lowest band use (BANDS -1) Now, if you save z to a cvs file or something similar and then use open office/excel/gnuplot to draw a graph of the data, you should see a representation of your song. If you pulled out the lowest band, asuming your song has clear drum beats (use a dance track for best results) you should be able to count the beats in the song.

To get a count of the beats i decided upon the following algorithm.

sum all values in z (from above)
divide by len(z)
loop through z and count all values greater than average * 2.49

Code:
total = 0
for i in z:
	total += i
avg = total / len(z)
cutoff = avg * 2.49
cnt = 0
for i in z:
	if i > cutoff:
		cnt+=1
print cnt
This got me the same result as counting the beats manually on the graph.

NEXT:
  • Cleaned up version of all the code to get this far.
  • some way of calculating bpm... (can't get a correct value for track length out of pymedia atm)
  • considerations for mixing.

Submit "PyDj: a project to write a better DJ in python than work in local clubs." to Digg Submit "PyDj: a project to write a better DJ in python than work in local clubs." to del.icio.us Submit "PyDj: a project to write a better DJ in python than work in local clubs." to StumbleUpon Submit "PyDj: a project to write a better DJ in python than work in local clubs." to Google

Categories
/dev , /random

Comments

  1. Chillz's Avatar
    look up virtual dj's (i think its "cue" now) automixing thing...same idea of what you have and it works perfectly


    (and your right beatmatching isnt that hard)
  2. Carnage's Avatar
    i've used the trial version of that but the point was to write it myself (and not have to pay for something)
  3. Chillz's Avatar
    i could find you a link to a full free version
  4. Carnage's Avatar
    still defeats the point of writing it yourself