In this article we’ll take a look at how to create a basic b.p.m. calculator based on sample rate and tempo. This information could be used as the foundation for a step sequencer or other b.p.m. based music arranger. A basic understanding of digital audio would be very helpful to understanding the following, but I’ll attempt to make it as painless as possible.
The first concept to understand is how digital audio is read from memory. Very simply, you can imagine a digital audio signal as a series of numbers, ones and zeros actually. Over time that series is read by a device and ultimately sent to your speakers as audible sound. There is a time interval at which the data is read. This is called the sample rate. The sample rate determines how many samples of data are read in a given time frame. An audio sample is not a one or a zero though. Depending on the bit-rate of the audio, the sample size changes. For instance in 16-bit audio, one sample is sixteen ones and zeroes. In this example we will be working with 16-bit audio at a sampling rate of 44,100 samples per second. To put it another way, every second of audio we are working with is made up of 44,100 samples. I’m skimming over some otherwise very interesting topics here, so we can quickly get to how it applies to the topic of calculating beats per minute.
For this topic we need not concern ourselves with the intricate details of bits and bytes and instead, we’ll focus on the concept of sample rates as applied to b.p.m. What we are most interested in, is where notes begin in time. This is what creates the sense of pulse and rhythm.

The above graphic represents the concept of calculating the space between notes so as to determine where notes begin. For the sake of this example lets assume that a note is one pulse of a bass drum ( much nicer than the click of a metronome ). Because we know that 44.1k samples occur in one second we can then deduce the the starting place for each note at a given tempo. For example lets say that the b.p.m. or our audio is one beat per second or 60 b.p.m. That means that a whole note would occur once every ( 44.1k * 4 ) samples and a quarter note would occur once every 44,100 samples.
Lets bump up the b.p.m. for the rest of this example to 90 b.p.m. and determine the interval at which to begin a quarter note. To do that we use a little math. If we multiply the sample rate by 60 ( as in 60 seconds in a minute, we get the number of samples in a minute ).
Samples per minute
44100 * 60 = 2646000
Next, we can divide the samples per minute by the b,p.m. This results in the interval at which a quarter note begins at the given b.p.m.
Sample interval for 90 b.p.m
SPM = samples per minute
T = sample interval
SPM / BPM = T
( 44100 * 60 ) / 90 = 29400
In the above example we determine that the sample interval for a 90 b.p.m. pulse is 29400 samples. This seems like a good time to start looking at how this might be accomplished through code. Here is some pseudo code that demonstrates the idea.
int position = 0; // -- this is an index that keeps track of where we are in time, incremented for every sample processed
int buffersize = 8192; // -- how many samples to create every time process() is called
int beat = 0;
int bpm = 90;
int samplerate = 44100;
int interval = ( samplerate * 60 ) / bpm;
function process()
{
for( i = 0; i < buffersize; i++ )
{
// -- if the remainder of the position by the interval is 0, we are at a sample where a quarter note begins
if( position % interval == 0 ) {
// -- NEW NOTE STARTS HERE!
beat++;
}
position++;
}
}
The above pseudo code demonstrates how to go about finding the position at which quarter notes begin. This works well enough, but what if we'd like to add notes other than quarter notes. This can be accomplished by using a very similar technique to the previous example. This time we will make the interval shorter by dividing the tempo by as many non-quater notes as we want. If our b.p.m. is 120 and we want 16th notes we can multiple the tempo by 16 and decrease the interval.
Sample interval for 90 b.p.m with 16th notes
SPM = samples per minute
T = sample interval
ST = steps ( 8th/16th/32nd notes )
SPM / ( BPM * ST ) = T
( 44100 * 60 ) / ( 90 * 16 ) = 1838 ( rounded up )
Taking the modulus of ST ( steps ) by the position by interval will produce a value representing the current beat. Again, here is a snippet of pseudo code representing this concept.
int position = 0; // -- this is an index that keeps track of where we are in time, incremented for every sample processed
int buffersize = 0; // -- how many samples to create every time process() is called
int beat = 0;
int bpm = 90;
int samplerate = 44100;
int steps = 16; // -- the number of sub-notes or steps in our sequence
int interval = ( samplerate * 60 ) / ( bpm * steps ) // -- decreasing the step interval by multiplying by steps
int currentStep = -1;
function process()
{
for( i = 0; i < buffersize; i++ )
{
// -- this time we use the modulus of the steps by position by interval
int newStep = ( position / interval ) % steps
if( newStep != currentStep ) {
// -- NEW NOTE STARTS HERE!
currentStep = newStep;
}
position++;
}
}
The above example now allows us to create a pulse with as many sub-quarter notes as we want. The next step would be to take measures and time signatures into account. This post is rather lengthy. I'll save measures and time signatures for the next post. In which, I'll include an actual code implementation. Thanks to Balazs from 28-inch for inspiring this post.. Also, I wrote a simple metronome that demonstrates some of these concepts back in June. It could make a nice next step in understanding b.p.m. calculations.
Continued Reading: