# Calculating bpm from machine sound (not music)

I have a hydraulic breaker on a excavator that hammers with a dynamic bpm. I have recorded this sound so that it is in a mp3-file and would like to determine the momentary bpm for the length of the file.

I have tried using processing with beat detection from the minim-library but so far without any good results.

Is using beat detection with energy mode in a selected band the preferred way to go? I have had troubles getting good information about where, in terms of frequency, the beats are.

An example of the sound could come from http://www.youtube.com/watch?v=596Dtm95Txo

## migrated from stackoverflow.comOct 4 '12 at 14:06

This question came from our site for professional and enthusiast programmers.

• Depending on how accurate you need this to be you could probably just use an FFT to generate a power spectrum and then identify the main peak. For greater accuracy and faster tracking a PLL might be a better approach. Flagging to move this to DSP.SE as it's more a DSP question than a programming question. – Paul R Oct 4 '12 at 9:29

First I would just try to detect peaks and measure the time between them. If there's too much background noise to measure them reliably, I'd try to identify frequencies that are strong in the pulses only and filter out everything else. If that didn't work, I'd try to do a sliding window auto-correlation:

1. Identify the longest gap between consecutive pulses
2. Using a window that is wide enough to capture the entirety of these two pulses:
3. Perform autocorrelation of the windowed signal (padded, not circular)
4. Find the peak in the autocorrelation = time delay between pulses
5. Convert from time offset to BPM
6. Put in output array along with offset of window
7. Slide the window to the right (maybe with overlap?)
8. Repeat from step 3

Actually, if the sounds you're looking for are similar enough, you can just use a regular cross-correlation, which will act like a sound-specific filter:

1. From the recording, isolate one of the clang sounds, use that as your template.
2. Cross-correlate the template with the template with the recording
3. Every time the clang occurs in the recording, it will produce a large spike in the correlation, while noise or other sounds will not.
4. Then it will be easier to find the peaks using a simple threshold and count the period T (samples per beat) between them, which you can then convert to BPM

fs (samples/second) / T (samples/beat) * 60 (seconds/minute) = beats/minute

I tried it with your YouTube recording but the clangs are not uniform enough to match perfectly. They match well in the region of time from which I took the template, but not in earlier parts of the file:

Recording:

Template:

Cross-correlation:

from scikits.audiolab import wavread
from scipy.signal import fftconvolve
from matplotlib.pyplot import plot

template, fs, enc = wavread('clang.wav')
recording, fs, enc = wavread('Volvo EC360B NLC clangs.wav')

# Cross-correlation is convolution with one reversed
c = fftconvolve(template[::-1], recording)

plot(abs(c))


There are two differences between your problem and music beat detection:

• The beat in music is a "latent" grid. It's not because a track is at 120BPM that you have exactly 2 notes per second. In music, the beat is more of a lowest common periodicity than an exact pulse train - that's why beat trackers often make octave errors, and also why there is no consensus between human listeners as to what is the BPM of some tracks. Your task doesn't suffer from the same ambiguity because every "hit" counts.
• The beat in music rarely changes over time; and when it changes, it is often progressive. As a result, the tempo can be assumed constant over windows of 5 or 6s for music. Judging from the video, you cannot make this assumption; because the "rhythm" here is very irregular; with pauses/breaks, and frequent changes.

Because of these, I fear that all the assumptions embedded into beat tracking algorithms made them useless on your problem. My suggestion would be to just use an onset detector with a high threshold (to make it more robust to noise). The events you want to detect seem to cover a rather wide frequency band anyway and have a fast decaying envelope.

Then, simply measure the time delta between adjacent events (when this delta is too high; consider that this is a pause); and eventually, average this over groups of a couple events.

• As suggested in the other answer, if this fails you may need to do some filtering to make it work. I don't think autocorrelation will work because if the abrupt changes in "tempo", but for small periods it might. – Bjorn Roche Oct 5 '12 at 15:06