```function [t,xcr] = beat_tempo(onsetenv,osr,disp)
% [t,xcr] = beat_tempo(onsetenv, osr, disp)
%   Estimate the tempo (in BPM) of the onset envelope onsetenv (at
%   frame rate osr).
%   xcr returns the cross-correlation from which the tempo peak was
%   picked.  We plot it if disp == 1.
% 2012-03-27 Dan Ellis dpwe@ee.columbia.edu

if nargin < 3; disp = 0; end

% autoco out to 4 s
acmax = round(4*osr);

% Find rough global period
% Only use the 2nd 60 sec to estimate global pd (avoid glitches?)

maxd = 60;
maxt = 120; % sec
maxcol = min(round(maxt*osr),length(onsetenv));
mincol = max(1,maxcol-round(maxd*osr));

xcr = xcorr(onsetenv(mincol:maxcol),onsetenv(mincol:maxcol),acmax);

% find local max in the global ac
rawxcr = xcr(acmax+1+[0:acmax]);

% window it around default bpm
% BPM prior is centered on bpmmean, with a log_2-domain Gaussian
% halfwidth of bpmsd
bpmmean = 120;
bpmsd = 0.9;

% What BPM does each bin of the autocorrelation correspond to?
bpms = 60*osr./([0:acmax]+0.1);
% Calculate the log-normal windowing
xcrwin = exp(-.5*((log(bpms/bpmmean)/log(2)/bpmsd).^2));

% Apply the weighting
xcr = rawxcr.*xcrwin;

xpkix = min(find(xcr == max(xcr)));

% Convert period to BPM
t = 60/(xpkix / osr);

if disp
plot([1:length(xcr)]/osr,xcr);
ax = axis;
hold on;
plot([1 1]*xpkix/osr, [ax(3) ax(4)], '-r');
hold off;
title(['Onset function autocorrelation, tempo = ', ...
sprintf('%.1f',t),' BPM']);
xlabel('lag / sec');
end
```