![](/i/fill.gif) |
![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
I found this:
http://www.all8.com/tools/bpm.htm
That's quite nice, but a cursory inspection of the JavaScript code
reveals how it works: In effect, the first keypress starts a timer. The
displayed BPM is simply the time elapsed between the first and most
recent keypress divided by the total number of presses.
In other words, if the first keypress is slightly out of time, it throws
the whole measurement off. Given that it usually takes a few moments to
get into the rhythm, the *first* keypress is the one *least likely* to
be precisely in time.
Now I'm wondering if I can come up with something a little more
accurate. (Although, obviously, accuracy is ultimately determined by how
good your coordination and sense of rhythm are!)
I had a play with this yesterday. I took my rock and roll collection and
measured the speed of 34 tracks.
Of course, BPM is slightly subjective in that it varies according to
what you deem a "beat" to be. For example, taking a waltz such as Hey
Paula, if you count the 1/2/3/4 beats you get approximately 75 BPM. If
you count the 1/2/3 beats inbetween as well, you get about 240 BPM.
(Roughly 3x faster - shockingly enough.)
Even for songs in 4/4 time (i.e., the vast majority of the tracks), you
usually have a choice of several BPM ratings, all multiples of 2 of each
other. I took my timings based on the speed of the "slow" steps you'd
most "naturall" dance to the music.
Using this measurement, I got speeds of around about 70 - 80 BPM for
most tracks. A few were as slow as 65 BPM, a couple were in the 90 to 95
BPM range.
And just *one* track registered at a frankly silly 135 BPM. (!!)
Arguably all those values ought to be multiplied by 2, since the "fast"
dance steps are twice the speed of the "slow" ones, and more closely
match the most obvious drum beats. That gives us most tracks being
somewhere between 120 BPM and 180 BPM, with one outlier at 270 BPM.
The outlier was Lonnie Donegan singing Cumberland Gap, by the way. I
literally can't move my feet that fast. I bet Debbie can...
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
> Now I'm wondering if I can come up with something a little more accurate.
> (Although, obviously, accuracy is ultimately determined by how good your
> coordination and sense of rhythm are!)
Oh, I thought this was going to be about how you wrote a Haskell program to
read from the line-in on your sound card in real time and display the BPM to
insance accuracy... ;-)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
scott wrote:
>> Now I'm wondering if I can come up with something a little more
>> accurate. (Although, obviously, accuracy is ultimately determined by
>> how good your coordination and sense of rhythm are!)
>
> Oh, I thought this was going to be about how you wrote a Haskell program
> to read from the line-in on your sound card in real time and display the
> BPM to insance accuracy... ;-)
Now come on, WHY would I do that?
...the sound is after all coming *from* my PC. ;-) The logical thing to
do with be to decode the Vorbis files back into WAV files and analyse
the resulting spectrum. Although this is far, far less trivial than
you'd imagine. (E.g., WinAmp can't reliably decide what the beat rate is
either, mainly because it can't "hear" the beats reliably.)
I was going to sit and write some elaborate Haskell thing using a GTK
front-end. But seeing how easy it is to do this with JavaScript and
HTML, it's quite obvious that this is the best way to proceed.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
> ...the sound is after all coming *from* my PC. ;-)
Oh I mistakenly thought I read somewhere that you had your collection on
real CDs. Anyway, the difference is just one parameter in an API call.
> The logical thing to do with be to decode the Vorbis files back into WAV
> files and analyse the resulting spectrum.
Would be more flexible to just read the mixed output from the soundcard,
that way your program would calculate the BPM of whatever music your
computer happened to be playing. You wouldn't have to worry about decoding
various formats either then.
> Although this is far, far less trivial than you'd imagine. (E.g., WinAmp
> can't reliably decide what the beat rate is either, mainly because it
> can't "hear" the beats reliably.)
Yeh I guess you'd need a good algorithm for detecting beats - presumably
some kind of autocorrelation function on certain parts of the spectrum, easy
to do both in one shot with FFTs), seems right up your street :-)
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
scott wrote:
>> ...the sound is after all coming *from* my PC. ;-)
>
> Oh I mistakenly thought I read somewhere that you had your collection on
> real CDs.
I did. Right before I transcoded them...
>> The logical thing to do with be to decode the Vorbis files back into
>> WAV files and analyse the resulting spectrum.
>
> Would be more flexible to just read the mixed output from the soundcard,
> that way your program would calculate the BPM of whatever music your
> computer happened to be playing.
Only problem is that first I'd have to find out what the necessary API
calls are. This is highly likely to be quite intractibly difficult.
> You wouldn't have to worry about decoding various formats either then.
It's easy enough to decode Vorbis (or just use the original rip files).
That way I only have to worry about WAV. (This is more complicated than
it sounds!)
>> Although this is far, far less trivial than you'd imagine. (E.g.,
>> WinAmp can't reliably decide what the beat rate is either, mainly
>> because it can't "hear" the beats reliably.)
>
> Yeh I guess you'd need a good algorithm for detecting beats - presumably
> some kind of autocorrelation function on certain parts of the spectrum,
> easy to do both in one shot with FFTs), seems right up your street :-)
The thing that seems to throw WinAmp is that most drumbeats are fairly
complicated, with double beats, fills and breaks, and so on.
Really, the human brain is far, far better at beat detection than any
computer. Hell, I can even tap out the beat during a break where there
is no audible beat at all! The computer has no hope of coping with that.
It then becomes a question of statistics. Just how accurately can you
decide where the beats are, assuming all the tap signals contain a
certain amount of jitter?
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
> Only problem is that first I'd have to find out what the necessary API
> calls are. This is highly likely to be quite intractibly difficult.
It's not *that* hard to use the Windows wave API to repeatedly get a buffer
full of samples, there are plenty of tutorials around and of course the
reference information from MS:
http://msdn.microsoft.com/en-us/library/aa909811.aspx
I managed two functions that easily fit on a single page to choose and open
a device for recording. However I switched to using ASIO drivers as the
performance was much better. No idea how difficult it is to use the ASIO
driver directly as someone had helpfully written a very simple-to-use .net
wrapper for it :-)
> That way I only have to worry about WAV. (This is more complicated than it
> sounds!)
I thought WAV was quite easy to decode, isn't the data just stored
byte-by-byte after some header?
> The thing that seems to throw WinAmp is that most drumbeats are fairly
> complicated, with double beats, fills and breaks, and so on.
Yeh, I can see how you would easily get an output was a multiple of the
actual BPM. But even with that information you could figure out the exact
BPM yourself (or fix your software to automatically multiply the BPM to be
in a certain range).
> Really, the human brain is far, far better at beat detection than any
> computer. Hell, I can even tap out the beat during a break where there is
> no audible beat at all! The computer has no hope of coping with that.
Depends how you wrote the program, for example if it couldn't find any beat
within a reasonable certainty it would continue on with the previous
estimate.
> It then becomes a question of statistics. Just how accurately can you
> decide where the beats are, assuming all the tap signals contain a certain
> amount of jitter?
That's easy, the hard bit is figuring out if the person missed a tap, or
pressed one too many times. This would give quite a large error (as opposed
to simply pressing all the taps half a second out).
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
scott wrote:
>> Only problem is that first I'd have to find out what the necessary API
>> calls are. This is highly likely to be quite intractibly difficult.
>
> It's not *that* hard to use the Windows wave API
O RLY?
Is this the same Windows API that takes a dozen API calls just to open a
plain ordinary window on screen and put a button in it?
>> That way I only have to worry about WAV. (This is more complicated
>> than it sounds!)
>
> I thought WAV was quite easy to decode, isn't the data just stored
> byte-by-byte after some header?
Yeah. The hard part is figuring out where the header ends.
(It's a RIFF file, which is an extension of the old Amiga IFF format. Or
rather, it's IFF but with all the 4-byte quantities written the wrong
way around.)
> Yeh, I can see how you would easily get an output was a multiple of the
> actual BPM.
Actually, BPM is an inherantly ambiguous measurement anyway, since it
varies according to what you consider to be a "beat".
> But even with that information you could figure out the
> exact BPM yourself (or fix your software to automatically multiply the
> BPM to be in a certain range).
That would mean that unusually fast or slow tracks would get measured
wrong. But hey... ;-)
>> It then becomes a question of statistics. Just how accurately can you
>> decide where the beats are, assuming all the tap signals contain a
>> certain amount of jitter?
>
> That's easy, the hard bit is figuring out if the person missed a tap, or
> pressed one too many times. This would give quite a large error (as
> opposed to simply pressing all the taps half a second out).
I'm thinking you could probably do this by looking at the taps and
rejecting any which vastly deviate from the others. But we'll see...
Initial testing indicates that I can tap with a standard deviation of
around about 20 ms, which isn't too bad. Interestingly, the SD is much
lower for fast tracks than slow ones. In all cases, I'm getting about +-
5% jitter.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
scott wrote:
>> Only problem is that first I'd have to find out what the necessary API
>> calls are. This is highly likely to be quite intractibly difficult.
>
> It's not *that* hard to use the Windows wave API to repeatedly get a
> buffer full of samples,
Hey, at least he didn't say it's obviously impossible. :-)
> I thought WAV was quite easy to decode, isn't the data just stored
> byte-by-byte after some header?
Yes. But the headers tell you important stuff like sample size and samples
per second and such.
--
Darren New, San Diego CA, USA (PST)
Eiffel - The language that lets you specify exactly
that the code does what you think it does, even if
it doesn't do what you wanted.
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
Invisible wrote:
> Now I'm wondering if I can come up with something a little more
> accurate. (Although, obviously, accuracy is ultimately determined by how
> good your coordination and sense of rhythm are!)
See attached. Press any key rhythmically to send "taps". The reset
button... uh... resets the stats.
It works by recording the amount of time between consecutive taps. As it
happens, taking the arithmetic mean of these numbers produces an
identical result to just taking the duration between first and last tap
and dividing by the number of taps. However, by storing the actual
times, I can also take the standard deviation.
(There's probably some way to compute the SD incrimentally. But, as it
happens, even after several hundred taps, there's no noticable problem
recomputing all the stats from scratch. Firefox uses about 0.5% CPU
while doing this. Which is impressive for a trivial scripting language.
I guess computers are just really, really fast these days!)
In my experiments, the error is always about +/- 4% or so. For fast
tracks there's less timing jitter, but greater timing precision is
required to get an accurate BPM reading, so the error is still about 4%.
(The "error" I'm quoting is just the size of one standard deviation.
ASSUMING A NORMAL DISTRIBUTION, this equates to 68% certainty.)
My next idea is to try generating a series of perfectly-spaced beats and
optimise for the best possible fit to all the taps...
Post a reply to this message
Attachments:
Download 'us-ascii' (4 KB)
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
> Hey, at least he didn't say it's obviously impossible. :-)
No - just ludicrously difficult. ;-)
>> I thought WAV was quite easy to decode, isn't the data just stored
>> byte-by-byte after some header?
>
> Yes. But the headers tell you important stuff like sample size and
> samples per second and such.
I might be mistaken, but I think it also specifies byte-order and
whether the samples are signed or unsigned - pretty damned important
info, that! ;-)
Still, I would also guess that, say, CDex is going to write identical
WAV files for every track ripped. So I can probably just go measure how
many bytes the header is, and skip that...
Post a reply to this message
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |
|
![](/i/fill.gif) |
| ![](/i/fill.gif) |
|
![](/i/fill.gif) |