wolffd@0: function varargout = mirtempo(x,varargin) wolffd@0: % t = mirtempo(x) evaluates the tempo in beats per minute (BPM). wolffd@0: % Optional arguments: wolffd@0: % mirtempo(...,'Total',m) selects not only the best tempo, but the m wolffd@0: % best tempos. wolffd@0: % mirtempo(...,'Frame',l,h) orders a frame decomposition of window wolffd@0: % length l (in seconds) and hop factor h, expressed relatively to wolffd@0: % the window length. For instance h = 1 indicates no overlap. wolffd@0: % Default values: l = 3 seconds and h = .1 wolffd@0: % mirtempo(...,'Min',mi) indicates the lowest tempo taken into wolffd@0: % consideration, expressed in bpm. wolffd@0: % Default value: 40 bpm. wolffd@0: % mirtempo(...,'Max',ma) indicates the highest tempo taken into wolffd@0: % consideration, expressed in bpm. wolffd@0: % Default value: 200 bpm. wolffd@0: % mirtempo(...,s) selects the tempo estimation strategy: wolffd@0: % s = 'Autocor': Approach based on the computation of the wolffd@0: % autocorrelation. (Default strategy) wolffd@0: % Option associated to the mirautocor function can be wolffd@0: % passed here as well (see help mirautocor): wolffd@0: % 'Enhanced' (toggled on by default here) wolffd@0: % s = 'Spectrum': Approach based on the computation of the wolffd@0: % spectrum . wolffd@0: % Option associated to the mirspectrum function can be wolffd@0: % passed here as well (see help mirspectrum): wolffd@0: % 'ZeroPad' (set by default to 10000 samples) wolffd@0: % 'Prod' (toggled off by default) wolffd@0: % These two strategies can be combined: the autocorrelation wolffd@0: % function is translated into the frequency domain in order wolffd@0: % to be compared to the spectrum curve. wolffd@0: % tempo(...,'Autocor','Spectrum') multiplies the two curves. wolffd@0: % Alternatively, an autocorrelation function ac or a spectrum sp wolffd@0: % can be directly passed to the function tempo: wolffd@0: % mirtempo(ac) or mirtempo(sp) wolffd@0: % The options related to the onset detection phase can be specified wolffd@0: % here as well (see help mironsets): wolffd@0: % onset detection strategies: 'Envelope', 'DiffEnvelope' wolffd@0: % (corresponding to 'Envelope', 'Diff'), 'SpectralFlux, wolffd@0: % 'Pitch', 'Log', 'Mu', 'Filterbank' wolffd@0: % mironsets(...,'Sum',w) specifies when to sum the channels. wolffd@0: % Possible values: wolffd@0: % w = 'Before': sum before the autocorrelation or wolffd@0: % spectrum computation. wolffd@0: % w = 'After': autocorrelation or spectrum computed wolffd@0: % for each band, and summed into a "summary". wolffd@0: % mirenvelope options: 'HalfwaveCenter','Diff' (toggled on by wolffd@0: % default here),'HalfwaveDiff','Center','Smooth', wolffd@0: % 'Sampling' wolffd@0: % mirflux options: 'Inc','Halfwave','Complex','Median' wolffd@0: % mirtempo(...,'Resonance',r) specifies the resonance curve, which wolffd@0: % emphasizes the periods that are more easily perceived. wolffd@0: % Possible values: 'ToiviainenSnyder' (default), 0 (toggled off) wolffd@0: % Optional arguments used for the peak picking (cf. help mirpeaks) wolffd@0: % mirtempo(...,'Contrast',thr): a threshold value. A given local wolffd@0: % maximum will be considered as a peak if its distance with the wolffd@0: % previous and successive local minima (if any) is higher than wolffd@0: % this threshold. This distance is expressed with respect to the wolffd@0: % total amplitude of the autocorrelation function. wolffd@0: % if no value for thr is given, the value thr=0.1 is chosen wolffd@0: % by default. wolffd@0: % wolffd@0: % [t,p] = mirtempo(...) also displays the result of the signal analysis wolffd@0: % leading to the tempo estimation, and shows in particular the wolffd@0: % peaks corresponding to the tempo values. wolffd@0: wolffd@0: wolffd@0: sum.key = 'Sum'; wolffd@0: sum.type = 'String'; wolffd@0: sum.choice = {'Before','After','Adjacent',0}; wolffd@0: sum.default = 'Before'; wolffd@0: option.sum = sum; wolffd@0: wolffd@0: %% options related to mironsets: wolffd@0: wolffd@0: frame.key = 'Frame'; wolffd@0: frame.type = 'Integer'; wolffd@0: frame.number = 2; wolffd@0: frame.default = [0 0]; wolffd@0: frame.keydefault = [3 .1]; wolffd@0: option.frame = frame; wolffd@0: wolffd@0: fea.type = 'String'; wolffd@0: fea.choice = {'Envelope','DiffEnvelope','SpectralFlux','Pitch'}; wolffd@0: fea.default = 'Envelope'; wolffd@0: option.fea = fea; wolffd@0: wolffd@0: %% options related to 'Envelope': wolffd@0: wolffd@0: envmeth.key = 'Method'; wolffd@0: envmeth.type = 'String'; wolffd@0: envmeth.choice = {'Filter','Spectro'}; wolffd@0: envmeth.default = 'Filter'; wolffd@0: option.envmeth = envmeth; wolffd@0: wolffd@0: %% options related to 'Filter': wolffd@0: wolffd@0: fb.key = 'Filterbank'; wolffd@0: fb.type = 'Integer'; wolffd@0: fb.default = 10; wolffd@0: option.fb = fb; wolffd@0: wolffd@0: fbtype.key = 'FilterbankType'; wolffd@0: fbtype.type = 'String'; wolffd@0: fbtype.choice = {'Gammatone','Scheirer','Klapuri'}; wolffd@0: fbtype.default = 'Gammatone'; wolffd@0: option.fbtype = fbtype; wolffd@0: wolffd@0: ftype.key = 'FilterType'; wolffd@0: ftype.type = 'String'; wolffd@0: ftype.choice = {'IIR','HalfHann'}; wolffd@0: ftype.default = 'IIR'; wolffd@0: option.ftype = ftype; wolffd@0: wolffd@0: %% options related to 'Spectro': wolffd@0: wolffd@0: band.type = 'String'; wolffd@0: band.choice = {'Freq','Mel','Bark','Cents'}; wolffd@0: band.default = 'Freq'; wolffd@0: option.band = band; wolffd@0: wolffd@0: wolffd@0: chwr.key = 'HalfwaveCenter'; wolffd@0: chwr.type = 'Boolean'; wolffd@0: chwr.default = 0; wolffd@0: option.chwr = chwr; wolffd@0: wolffd@0: diff.key = 'Diff'; wolffd@0: diff.type = 'Boolean'; wolffd@0: diff.default = 1; % Different default for mirtempo wolffd@0: option.diff = diff; wolffd@0: wolffd@0: diffhwr.key = 'HalfwaveDiff'; wolffd@0: diffhwr.type = 'Integer'; wolffd@0: diffhwr.default = 0; wolffd@0: diffhwr.keydefault = 1; wolffd@0: option.diffhwr = diffhwr; wolffd@0: wolffd@0: lambda.key = 'Lambda'; wolffd@0: lambda.type = 'Integer'; wolffd@0: lambda.default = 1; wolffd@0: option.lambda = lambda; wolffd@0: wolffd@0: mu.key = 'Mu'; wolffd@0: mu.type = 'Boolean'; wolffd@0: mu.default = 0; wolffd@0: option.mu = mu; wolffd@0: wolffd@0: log.key = 'Log'; wolffd@0: log.type = 'Boolean'; wolffd@0: log.default = 0; wolffd@0: option.log = log; wolffd@0: wolffd@0: c.key = 'Center'; wolffd@0: c.type = 'Boolean'; wolffd@0: c.default = 0; wolffd@0: option.c = c; wolffd@0: wolffd@0: aver.key = 'Smooth'; wolffd@0: aver.type = 'Integer'; wolffd@0: aver.default = 0; wolffd@0: aver.keydefault = 30; wolffd@0: option.aver = aver; wolffd@0: wolffd@0: sampling.key = 'Sampling'; wolffd@0: sampling.type = 'Integer'; wolffd@0: sampling.default = 0; wolffd@0: option.sampling = sampling; wolffd@0: wolffd@0: %% options related to 'SpectralFlux' wolffd@0: wolffd@0: complex.key = 'Complex'; wolffd@0: complex.type = 'Boolean'; wolffd@0: complex.default = 0; wolffd@0: option.complex = complex; wolffd@0: wolffd@0: inc.key = 'Inc'; wolffd@0: inc.type = 'Boolean'; wolffd@0: inc.default = 1; wolffd@0: option.inc = inc; wolffd@0: wolffd@0: median.key = 'Median'; wolffd@0: median.type = 'Integer'; wolffd@0: median.number = 2; wolffd@0: median.default = [.2 1.3]; wolffd@0: option.median = median; wolffd@0: wolffd@0: hw.key = 'Halfwave'; wolffd@0: hw.type = 'Boolean'; wolffd@0: hw.default = 1; wolffd@0: option.hw = hw; wolffd@0: wolffd@0: wolffd@0: %% options related to mirautocor: wolffd@0: wolffd@0: aut.key = 'Autocor'; wolffd@0: aut.type = 'Integer'; wolffd@0: aut.default = 0; wolffd@0: aut.keydefault = 1; wolffd@0: option.aut = aut; wolffd@0: wolffd@0: nw.key = 'NormalWindow'; wolffd@0: nw.default = 0; wolffd@0: option.nw = nw; wolffd@0: wolffd@0: enh.key = 'Enhanced'; wolffd@0: enh.type = 'Integers'; wolffd@0: enh.default = 2:10; wolffd@0: enh.keydefault = 2:10; wolffd@0: option.enh = enh; wolffd@0: wolffd@0: r.key = 'Resonance'; wolffd@0: r.type = 'String'; wolffd@0: r.choice = {'ToiviainenSnyder','vanNoorden',0,'off','no'}; wolffd@0: r.default = 'ToiviainenSnyder'; wolffd@0: option.r = r; wolffd@0: wolffd@0: wolffd@0: %% options related to mirspectrum: wolffd@0: wolffd@0: spe.key = 'Spectrum'; wolffd@0: spe.type = 'Integer'; wolffd@0: spe.default = 0; wolffd@0: spe.keydefault = 1; wolffd@0: option.spe = spe; wolffd@0: wolffd@0: zp.key = 'ZeroPad'; wolffd@0: zp.type = 'Integer'; wolffd@0: zp.default = 10000; wolffd@0: zp.keydefault = Inf; wolffd@0: option.zp = zp; wolffd@0: wolffd@0: prod.key = 'Prod'; wolffd@0: prod.type = 'Integers'; wolffd@0: prod.default = 0; wolffd@0: prod.keydefault = 2:6; wolffd@0: option.prod = prod; wolffd@0: wolffd@0: wolffd@0: %% options related to the peak detection wolffd@0: wolffd@0: m.key = 'Total'; wolffd@0: m.type = 'Integer'; wolffd@0: m.default = 1; wolffd@0: option.m = m; wolffd@0: wolffd@0: thr.key = 'Contrast'; wolffd@0: thr.type = 'Integer'; wolffd@0: thr.default = 0.1; wolffd@0: option.thr = thr; wolffd@0: wolffd@0: mi.key = 'Min'; wolffd@0: mi.type = 'Integer'; wolffd@0: mi.default = 40; wolffd@0: option.mi = mi; wolffd@0: wolffd@0: ma.key = 'Max'; wolffd@0: ma.type = 'Integer'; wolffd@0: ma.default = 200; wolffd@0: option.ma = ma; wolffd@0: wolffd@0: track.key = 'Track'; wolffd@0: track.type = 'Boolean'; wolffd@0: track.default = 0; wolffd@0: option.track = track; wolffd@0: wolffd@0: pref.key = 'Pref'; wolffd@0: pref.type = 'Integer'; wolffd@0: pref.number = 2; wolffd@0: pref.default = [0 .2]; wolffd@0: option.pref = pref; wolffd@0: wolffd@0: perio.key = 'Periodicity'; wolffd@0: perio.type = 'Boolean'; wolffd@0: perio.default = 0; wolffd@0: option.perio = perio; wolffd@0: wolffd@0: specif.option = option; wolffd@0: wolffd@0: varargout = mirfunction(@mirtempo,x,varargin,nargout,specif,@init,@main); wolffd@0: wolffd@0: wolffd@0: %% INIT wolffd@0: wolffd@0: function [y type] = init(x,option) wolffd@0: if iscell(x) wolffd@0: x = x{1}; wolffd@0: end wolffd@0: if option.perio wolffd@0: option.m = 3; wolffd@0: option.enh = 2:10; wolffd@0: end wolffd@0: if not(isamir(x,'mirautocor')) && not(isamir(x,'mirspectrum')) wolffd@0: if isframed(x) && strcmpi(option.fea,'Envelope') && not(isamir(x,'mirscalar')) wolffd@0: warning('WARNING IN MIRTEMPO: The input should not be already decomposed into frames.'); wolffd@0: disp(['Suggestion: Use the ''Frame'' option instead.']) wolffd@0: end wolffd@0: if strcmpi(option.sum,'Before') wolffd@0: optionsum = 1; wolffd@0: elseif strcmpi(option.sum,'Adjacent') wolffd@0: optionsum = 5; wolffd@0: else wolffd@0: optionsum = 0; wolffd@0: end wolffd@0: if option.frame.length.val wolffd@0: x = mironsets(x,option.fea,'Filterbank',option.fb,... wolffd@0: 'FilterbankType',option.fbtype,... wolffd@0: 'FilterType',option.ftype,... wolffd@0: 'Sum',optionsum,'Method',option.envmeth,... wolffd@0: option.band,'Center',option.c,... wolffd@0: 'HalfwaveCenter',option.chwr,'Diff',option.diff,... wolffd@0: 'HalfwaveDiff',option.diffhwr,'Lambda',option.lambda,... wolffd@0: 'Smooth',option.aver,'Sampling',option.sampling,... wolffd@0: 'Complex',option.complex,'Inc',option.inc,... wolffd@0: 'Median',option.median(1),option.median(2),... wolffd@0: 'Halfwave',option.hw,'Detect',0,... wolffd@0: 'Mu',option.mu,'Log',option.log,... wolffd@0: 'Frame',option.frame.length.val,... wolffd@0: option.frame.length.unit,... wolffd@0: option.frame.hop.val,... wolffd@0: option.frame.hop.unit); wolffd@0: else wolffd@0: x = mironsets(x,option.fea,'Filterbank',option.fb,... wolffd@0: 'FilterbankType',option.fbtype,... wolffd@0: 'FilterType',option.ftype,... wolffd@0: 'Sum',optionsum,'Method',option.envmeth,... wolffd@0: option.band,'Center',option.c,... wolffd@0: 'HalfwaveCenter',option.chwr,'Diff',option.diff,... wolffd@0: 'HalfwaveDiff',option.diffhwr,'Lambda',option.lambda,... wolffd@0: 'Smooth',option.aver,'Sampling',option.sampling,... wolffd@0: 'Complex',option.complex,'Inc',option.inc,... wolffd@0: 'Median',option.median(1),option.median(2),... wolffd@0: 'Halfwave',option.hw,'Detect',0,... wolffd@0: 'Mu',option.mu,'Log',option.log); wolffd@0: end wolffd@0: end wolffd@0: if option.aut == 0 && option.spe == 0 wolffd@0: option.aut = 1; wolffd@0: end wolffd@0: if isamir(x,'mirautocor') || (option.aut && not(option.spe)) wolffd@0: y = mirautocor(x,'Min',60/option.ma,'Max',60/option.mi,... wolffd@0: 'Enhanced',option.enh,...'NormalInput','coeff',... wolffd@0: 'Resonance',option.r,'NormalWindow',option.nw); wolffd@0: elseif isamir(x,'mirspectrum') || (option.spe && not(option.aut)) wolffd@0: y = mirspectrum(x,'Min',option.mi/60,'Max',option.ma/60,... wolffd@0: 'Prod',option.prod,...'NormalInput',... wolffd@0: 'ZeroPad',option.zp,'Resonance',option.r); wolffd@0: elseif option.spe && option.aut wolffd@0: ac = mirautocor(x,'Min',60/option.ma,'Max',60/option.mi,... wolffd@0: 'Enhanced',option.enh,...'NormalInput','coeff',... wolffd@0: 'Resonance',option.r); wolffd@0: sp = mirspectrum(x,'Min',option.mi/60,'Max',option.ma/60,... wolffd@0: 'Prod',option.prod,...'NormalInput',... wolffd@0: 'ZeroPad',option.zp,'Resonance',option.r); wolffd@0: y = ac*sp; wolffd@0: end wolffd@0: if ischar(option.sum) wolffd@0: y = mirsum(y); wolffd@0: end wolffd@0: y = mirpeaks(y,'Total',option.m,'Track',option.track,... wolffd@0: 'Pref',option.pref(1),option.pref(2),... wolffd@0: 'Contrast',option.thr,'NoBegin','NoEnd',... wolffd@0: 'Normalize','Local'); wolffd@0: type = {'mirscalar',mirtype(y)}; wolffd@0: wolffd@0: wolffd@0: %% MAIN wolffd@0: wolffd@0: function o = main(p,option,postoption) wolffd@0: if iscell(p) wolffd@0: p = p{1}; wolffd@0: end wolffd@0: pt = get(p,'TrackPrecisePos'); wolffd@0: track = 1; wolffd@0: if isempty(pt) || isempty(pt{1}) wolffd@0: pt = get(p,'PeakPrecisePos'); wolffd@0: track = 0; wolffd@0: end wolffd@0: bpm = cell(1,length(pt)); wolffd@0: for j = 1:length(pt) wolffd@0: bpm{j} = cell(1,length(pt{j})); wolffd@0: for k = 1:length(pt{j}) wolffd@0: ptk = pt{j}{k}; wolffd@0: bpmk = cell(1,size(ptk,2)); wolffd@0: for h = 1:size(ptk,3) wolffd@0: for l = 1:size(ptk,2) wolffd@0: ptl = ptk{1,l,h}; wolffd@0: if isempty(ptl) wolffd@0: bpmk{1,l,h} = NaN; wolffd@0: else wolffd@0: if isa(p,'mirautocor') && not(get(p,'FreqDomain')) wolffd@0: bpmk{1,l,h} = 60./ptl; wolffd@0: else wolffd@0: bpmk{1,l,h} = ptl*60; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if track wolffd@0: bpmk = bpmk{1}; wolffd@0: end wolffd@0: bpm{j}{k} = bpmk; wolffd@0: end wolffd@0: end wolffd@0: t = mirscalar(p,'Data',bpm,'Title','Tempo','Unit','bpm'); wolffd@0: o = {t,p};