# BPM Detection using Matlab xcorr function

Objective: Finding the BPM of a short drum loop.

Approach:

% Step 1: Get audio signal and sample rate
[audio_signal, Fs] = audioread(".\90-bpm.wav"); % Drum loop recorded at 90bpm
audio_signal = audio_signal(:,1); % Only use 1 channel
short_signal = audio_signal(1:1e5,:); % Get short version of sample

% Step 2: Get the audio signal length
audio_length = length(audio_signal);

% Step 3: Create the time vector based on the audio signal length and sampling rate
time = (0:audio_length-1) / Fs; % Used for plotting time axis

% Step 4: Plot the audio signal
plot(time, audio_signal);
xlabel('Time (seconds)');
ylabel('Amplitude');
title('Audio Signal');
grid on;

% Use correlation with a shorter version of the signal
[result, lags] = xcorr(audio_signal, short_signal);

plot(lags/Fs, result); % Normalized lag-axis
xlabel('Lag');
ylabel('xcrorr result');
grid on;

% Find the peaks at their locations
[peaks, locs] = findpeaks(result);


Result

Interpretation

The result shows two large peaks, where I assume the first one corresponds to the start of both samples (highest point of similarity at lag = 0) and the second largest peak where I assume both samples are most likely repeating itself.

Questions

I would like to understand how I can use this result to find the bpm of the drum loop. Would I have to find the time between each peak? How could I determine the bpm using the smaller peaks seen through the result? How does "lag/delay" translate to time?

EDIT: Added plotting lines to show normalization of lag scale.

FOLLOW-UP

% Use correlation with a shorter version of the signal
[result, lags] = xcorr(audio_signal, short_signal);

plot(lags, result); % Normalized lag-axis
xlabel('Lag');
ylabel('xcrorr result');
grid on;

% Find the peaks at their locations
[pksort, locsort] = findpeaks(result, lags, "SortStr", "descend");
pkpairs= pksort(1:2,:);
locpairs = locsort(:, 1:2);

loc_time = locpairs(:,2)/Fs;
beat_sec = 1/loc_time;


Result

>> locpairs/Fs

ans =

0    2.6667

>> loc_time = locpairs(:,2)/Fs;
>> loc_time

loc_time =

2.6667

>> beat_sec = 1/loc_time;
>> beat_sec

beat_sec =

0.3750

>> beat_sec * 60

ans =

22.5000


Original Signal

$$\texttt{BPM} = \frac{60 \cdot f_s}{\texttt{lag}}$$

Assuming a sampling frequency $$f_s=44100$$, we’d expect the highest correlation peak to be at $$\texttt{lag}\,29400$$, which is more or less in agreement with your correlation plot (the x-axis is missing a $$\times 10^5$$ scaling factor).

• The assumption is correct, the sampling frequency is 44.1kHz. Where does the 60 come from in the equation for the BPM? Aug 9 at 15:25
• @ChalupaBatmac $f_s/\texttt{lag}$ has units of beats/second, and there are 60 seconds in 1 minute. So you multiply by 60 to get beats/minute
– Jdip
Aug 9 at 16:54
• To give you more detail. Your first correlation peak is at lag (approximately, I can’t see the actual lag) $29000$. So in terms of seconds, that’s $29000/44100 \approx 0.66$ seconds. So the beat repeats every $0.66$ seconds which means there are $1/0.66 = 1.51$ beats per second, or $1.51 \times 60 \approx 90$ beats per minute. Don’t forget to mark the answer if you’re satisfied.
– Jdip
Aug 9 at 16:58