There's a task I have to do periodically which involves simple algebra and trivial arithmetic. But because I'm clumsy with the former and hopeless at the latter, I wrote a little Python script to do it.
Today I've got a sample loop that lasts 16.711 seconds, and I want it to fit with a track at 126 bpm. Not sure how many bars are in the loop, but both that and the track are in 4/4 time. Audacity supports tempo change (retaining same pitch), so I need to know possible tempos for the loop. It's usually the case that I want the loop to sound near to the original, so a bpm close to the original is preferred. Another consideration is that it's often the case that the loop contains a 'regular' number of bars (say 4 for a breakbeat, 12 for a bit of blues).
Before going any further, here's the script: tempo.py
To use it, as well as having Python you'll need to edit the script to change these values to your own (I can't be bothered adding the command-line handling bits just now) -
bpm = 126.0
loop_duration = 16.711
Then running it will yield something like:
bars tempo
9 129.26
8 114.89
10 143.62
7 100.53
11 157.98
6 86.17
12 172.34
5 71.81
13 186.70
4 57.45
14 201.07
15 215.43
16 229.79
17 244.15
The tempos are sorted by their proximity to that of the tune I want to bung the sample into.
I know the loop I'm playing with goes through a bit of a tune twice, so that 8 114.89 looks promising...I just ran it through Audacity (old tempo: 115, new tempo 126) and bunged the result in energyXT, worked a treat.
Now onto code style. I mostly use Python for quick & dirty stuff, rarely bothering with being remotely Pythonesque. But this time I thought I'd go for it (although I had to do a lot of googling to get the syntax right), leading to the lines:
tempos = [(barcount, get_bpm(loop_duration, barcount)) for barcount in range(low_barcount, high_barcount)]
sorted_tempos = sorted(tempos, key=lambda tempo: floor(abs(tempo[1]-bpm)))
- which are considerably more concise than I'd usually do such things. But to make that understandable for anyone that isn't particularly familiar with such stuff (including myself in a day or two), I'd have to add a pile of comments. On the other hand, if I'd gone with my usual lazy style (straightforward for...next, more explicit tuples, a def function rather than a lambda...), how it works would have been pretty obvious to anyone. Ok, it's nifty stuff, but looks a lot like comprehensions etc. reduce comprehension.