Mercurial > hg > camir-aes2014
diff toolboxes/MIRtoolbox1.3.2/MIRToolbox/mirpulseclarity.m @ 0:e9a9cd732c1e tip
first hg version after svn
author | wolffd |
---|---|
date | Tue, 10 Feb 2015 15:05:51 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolboxes/MIRtoolbox1.3.2/MIRToolbox/mirpulseclarity.m Tue Feb 10 15:05:51 2015 +0000 @@ -0,0 +1,450 @@ +function varargout = mirpulseclarity(orig,varargin) +% r = mirpulseclarity(x) estimates the rhythmic clarity, indicating the +% strength of the beats estimated by the mirtempo function. +% Optional arguments: +% mirpulseclarity(...,s): specifies a strategy for pulse clarity +% estimation. +% Possible values: 'MaxAutocor' (default), 'MinAutocor', +% 'KurtosisAutocor', MeanPeaksAutocor', 'EntropyAutocor', +% 'InterfAutocor', 'TempoAutocor', 'ExtremEnvelop', +% 'Attack', 'Articulation' +% mirpulseclarity(...,'Frame',l,h): orders a frame decomposition of +% the audio input of window length l (in seconds) and hop factor +% h, expressed relatively to the window length. +% Default values: l = 5 seconds and h = .1 +% Onset detection strategies: 'Envelope' (default), 'DiffEnvelope', +% 'SpectralFlux', 'Pitch'. +% Options related to the autocorrelation computation can be specified +% as well: 'Min', 'Max', 'Resonance', 'Enhanced' +% Options related to the tempo estimation can be specified here +% as well: 'Sum', 'Total', 'Contrast'. +% cf. User's Manual for more details. +% [r,a] = mirpulseclarity(x) also returns the beat autocorrelation. + + model.key = 'Model'; + model.type = 'Integer'; + model.default = 0; + option.model = model; + + stratg.type = 'String'; + stratg.choice = {'MaxAutocor','MinAutocor','MeanPeaksAutocor',... + 'KurtosisAutocor','EntropyAutocor',... + 'InterfAutocor','TempoAutocor','ExtremEnvelop',... + 'Attack','Articulation'}; ...,'AttackDiff' + stratg.default = 'MaxAutocor'; + option.stratg = stratg; + + frame.key = 'Frame'; + frame.type = 'Integer'; + frame.number = 2; + frame.keydefault = [5 .1]; + frame.default = [0 0]; + option.frame = frame; + +%% options related to mironsets: + + fea.type = 'String'; + fea.choice = {'Envelope','DiffEnvelope','SpectralFlux','Pitch'}; + fea.default = 'Envelope'; + option.fea = fea; + + + %% options related to 'Envelope': + + envmeth.key = 'Method'; + envmeth.type = 'String'; + envmeth.choice = {'Filter','Spectro'}; + envmeth.default = 'Spectro'; + option.envmeth = envmeth; + + %% options related to 'Filter': + + ftype.key = 'FilterType'; + ftype.type = 'String'; + ftype.choice = {'IIR','HalfHann'}; + ftype.default = 'IIR'; + option.ftype = ftype; + + fb.key = 'Filterbank'; + fb.type = 'Integer'; + fb.default = 20; + option.fb = fb; + + fbtype.key = 'FilterbankType'; + fbtype.type = 'String'; + fbtype.choice = {'Gammatone','Scheirer','Klapuri'}; + fbtype.default = 'Scheirer'; + option.fbtype = fbtype; + + %% options related to 'Spectro': + + band.type = 'String'; + band.choice = {'Freq','Mel','Bark','Cents'}; + band.default = 'Freq'; + option.band = band; + + + diffhwr.key = 'HalfwaveDiff'; + diffhwr.type = 'Integer'; + diffhwr.default = 0; + diffhwr.keydefault = 1; + option.diffhwr = diffhwr; + + lambda.key = 'Lambda'; + lambda.type = 'Integer'; + lambda.default = 1; + option.lambda = lambda; + + aver.key = 'Smooth'; + aver.type = 'Integer'; + aver.default = 0; + aver.keydefault = 30; + option.aver = aver; + + oplog.key = 'Log'; + oplog.type = 'Boolean'; + oplog.default = 0; + option.log = oplog; + + mu.key = 'Mu'; + mu.type = 'Boolean'; + mu.default = 1; + option.mu = mu; + + %% options related to 'SpectralFlux' + + inc.key = 'Inc'; + inc.type = 'Boolean'; + inc.default = 1; + option.inc = inc; + + median.key = 'Median'; + median.type = 'Integer'; + median.number = 2; + median.default = [0 0]; % Not same default as in mirtempo + option.median = median; + + hw.key = 'Halfwave'; + hw.type = 'Boolean'; + hw.default = 0; %NaN; %0; % Not same default as in mirtempo + option.hw = hw; + + +%% options related to mirattackslope + slope.type = 'String'; + slope.choice = {'Diff','Gauss'}; + slope.default = 'Diff'; + option.slope = slope; + +%% options related to mirautocor: + + enh.key = 'Enhanced'; + enh.type = 'Integers'; + enh.default = []; + enh.keydefault = 2:10; + option.enh = enh; + + r.key = 'Resonance'; + r.type = 'String'; + r.choice = {'ToiviainenSnyder','vonNoorden',0,'off','no'}; + r.default = 'ToiviainenSnyder'; + option.r = r; + + mi.key = 'Min'; + mi.type = 'Integer'; + mi.default = 40; + option.mi = mi; + + ma.key = 'Max'; + ma.type = 'Integer'; + ma.default = 200; + option.ma = ma; + +%% options related to mirtempo: + + sum.key = 'Sum'; + sum.type = 'String'; + sum.choice = {'Before','After','Adjacent'}; + sum.default = 'Before'; + option.sum = sum; + + m.key = 'Total'; + m.type = 'Integer'; + m.default = 1; + option.m = m; + + thr.key = 'Contrast'; + thr.type = 'Integer'; + thr.default = 0.01; % Not same default as in mirtempo + option.thr = thr; + +specif.option = option; + +varargout = mirfunction(@mirpulseclarity,orig,varargin,nargout,specif,@init,@main); + + + +%% Initialisation + +function [x type] = init(x,option) +%if isframed(x) +% warning('WARNING IN MIRPULSECLARITY: The input should not be already decomposed into frames.'); +% disp(['Suggestion: Use the ''Frame'' option instead.']) +%end +if iscell(x) + x = x{1}; +end +if isamir(x,'mirautocor') + type = {'mirscalar','mirautocor'}; +elseif length(option.model) > 1 + a = x; + type = {'mirscalar'}; + for m = 1:length(option.model) + if option.frame.length.val + y = mirpulseclarity(a,'Model',option.model(m),... + 'Frame',option.frame.length.val,... + option.frame.length.unit,... + option.frame.hop.val,... + option.frame.hop.unit); + else + y = mirpulseclarity(a,'Model',option.model(m)); + end + if m == 1 + x = y; + else + x = x + y; + end + end +else + if option.model + switch option.model + case 1 + case 2 + option.envmeth = 'Filter'; + option.fbtype = 'Gammatone'; + option.mu = 0; + option.r = 0; + option.lambda = .8; + option.sum = 'After'; + end + end + if length(option.stratg)>7 && strcmpi(option.stratg(end-6:end),'Autocor') + if (strcmpi(option.stratg,'MaxAutocor') || ... + strcmpi(option.stratg,'MinAutocor') || ... + strcmpi(option.stratg,'EntropyAutocor')) + option.m = 0; + end + if strcmpi(option.stratg,'MinAutocor') + option.enh = 0; + end + if option.frame.length.val + [t,x] = mirtempo(x,option.fea,'Method',option.envmeth,... + option.band,... + 'Sum',option.sum,'Enhanced',option.enh,... + 'Resonance',option.r,'Smooth',option.aver,... + 'HalfwaveDiff',option.diffhwr,... + 'Lambda',option.lambda,... + 'Frame',option.frame.length.val,... + option.frame.length.unit,... + option.frame.hop.val,... + option.frame.hop.unit,... + 'FilterbankType',option.fbtype,... + 'FilterType',option.ftype,... + 'Filterbank',option.fb,'Mu',option.mu,... + 'Log',option.log,... + 'Inc',option.inc,'Halfwave',option.hw,... + 'Median',option.median(1),option.median(2),... + 'Min',option.mi,'Max',option.ma,... + 'Total',option.m,'Contrast',option.thr); + else + [t,x] = mirtempo(x,option.fea,'Method',option.envmeth,... + option.band,... + 'Sum',option.sum,'Enhanced',option.enh,... + 'Resonance',option.r,'Smooth',option.aver,... + 'HalfwaveDiff',option.diffhwr,... + 'Lambda',option.lambda,... + 'FilterbankType',option.fbtype,... + 'FilterType',option.ftype,... + 'Filterbank',option.fb,'Mu',option.mu,... + 'Log',option.log,... + 'Inc',option.inc,'Halfwave',option.hw,... + 'Median',option.median(1),option.median(2),... + 'Min',option.mi,'Max',option.ma,... + 'Total',option.m,'Contrast',option.thr); + end + type = {'mirscalar','mirautocor'}; + elseif strcmpi(option.stratg,'ExtremEnvelop') + x = mironsets(x,'Filterbank',option.fb); + type = {'mirscalar','mirenvelope'}; + elseif strcmpi(option.stratg,'Attack') + x = mirattackslope(x,option.slope); + type = {'mirscalar','mirenvelope'}; +% elseif strcmpi(option.stratg,'AttackDiff') +% type = {'mirscalar','mirenvelope'}; + elseif strcmpi(option.stratg,'Articulation') + x = mirlowenergy(x,'ASR'); + type = {'mirscalar','mirscalar'}; + else + type = {'mirscalar','miraudio'}; + end +end + + + +%% Main function + +function o = main(a,option,postoption) +if option.model == 2 + option.stratg = 'InterfAutocor'; +end +if isa(a,'mirscalar') && not(strcmpi(option.stratg,'Attack')) % not very nice test... to improve. + o = {a}; + return +end +if option.m == 1 && ... + (strcmpi(option.stratg,'InterfAutocor') || ... + strcmpi(option.stratg,'MeanPeaksAutocor')) + option.m = Inf; +end +if iscell(a) + a = a{1}; +end +if strcmpi(option.stratg,'MaxAutocor') + d = get(a,'Data'); + rc = mircompute(@max,d); +elseif strcmpi(option.stratg,'MinAutocor') + d = get(a,'Data'); + rc = mircompute(@minusmin,d); +elseif strcmpi(option.stratg,'MeanPeaksAutocor') + m = get(a,'PeakVal'); + rc = mircompute(@meanpeaks,m); +elseif strcmpi(option.stratg,'KurtosisAutocor') + a = mirpeaks(a,'Extract','Total',option.m,'NoBegin','NoEnd'); + k = mirkurtosis(a); + %d = get(k,'Data'); + %rc = mircompute(@meanpeaks,d); + rc = mirmean(k); +elseif strcmpi(option.stratg,'EntropyAutocor') + rc = mirentropy(a); +elseif strcmpi(option.stratg,'InterfAutocor') + a = mirpeaks(a,'Total',option.m,'NoBegin','NoEnd'); + m = get(a,'PeakVal'); + p = get(a,'PeakPosUnit'); + rc = mircompute(@interf,m,p); +elseif strcmpi(option.stratg,'TempoAutocor') + a = mirpeaks(a,'Total',1,'NoBegin','NoEnd'); + p = get(a,'PeakPosUnit'); + rc = mircompute(@tempo,p); +elseif strcmpi(option.stratg,'ExtremEnvelop') + a = mirenvelope(a,'Normal'); + p = mirpeaks(a,'Order','Abscissa'); + p = get(p,'PeakPreciseVal'); + n = mirpeaks(a,'Valleys','Order','Abscissa'); + n = get(n,'PeakPreciseVal'); + rc = mircompute(@shape,p,n); +elseif strcmpi(option.stratg,'Attack') + rc = mirmean(a); +%elseif strcmpi(option.stratg,'AttackDiff') +% a = mirpeaks(a); +% m = get(a,'PeakVal'); +% rc = mircompute(@meanpeaks,m); +elseif strcmpi(option.stratg,'Articulation') + rc = a; +end + +if iscell(rc) + pc = mirscalar(a,'Data',rc,'Title','Pulse clarity'); +else + pc = set(rc,'Title',['Pulse clarity (',get(rc,'Title'),')']); +end + +if option.model + switch option.model + case 1 + alpha = 0; + beta = 2.2015; + lambda = .1; + case 2 + alpha = 0; + beta = 3.5982; + lambda = 1.87; + end + if not(lambda == 0) + pc = (pc+alpha)^lambda * beta; + else + pc = log(pc+alpha) * beta; + end + title = ['Pulse clarity (Model ',num2str(option.model),')']; + pc = set(pc,'Title',title); +end + +o = {pc a}; + + +%% Routines + +function r = shape(p,n) +p = p{1}; +n = n{1}; +if length(p)>length(n) + d = sum(p(1:end-1) - n) + sum(p(2:end) - n); + r = d/(2*length(n)); +elseif length(p)<length(n) + d = sum(p - n(1:end-1)) + sum(p - n(2:end)); + r = d/(2*length(p)); +else + d = sum(p(2:end) - n(1:end-1)) + sum(p(1:end-1) - n(2:end)); + r = d/(2*(length(p)-1)); +end + + +function rc = minusmin(ac) +rc = -min(ac); + + +function rc = meanpeaks(ac) +rc = zeros(1,length(ac)); +for j = 1:length(ac) + if isempty(ac{j}) + rc(j) = NaN; + else + rc(j) = mean(ac{j}); + end +end + + +function rc = interf(mk,pk) +rc = zeros(size(mk)); +for j = 1:size(mk,3) + for i = 1:size(mk,2) + pij = pk{1,i,j}; + mij = mk{1,i,j}; + if isempty(pij) + rc(1,i,j) = 0; + else + high = max(pij(2:end),pij(1)); + low = min(pij(2:end),pij(1)); + quo = rem(high,low)./low; + nomult = quo>.15 & quo<.85; + fij = mij(2:end)/mij(1) .*nomult; + fij(fij<0) = 0; + rc(1,i,j) = exp(-sum(fij)/4); % Pulsations that are not in integer ratio + % with dominant pulse decrease clarity + end + end +end + + +function rc = tempo(pk) +rc = zeros(size(pk)); +for j = 1:size(pk,3) + for i = 1:size(pk,2) + pij = pk{1,i,j}; + if isempty(pij) + rc(1,i,j) = 0; + else + rc(1,i,j) = exp(-pij(1)/4)/exp(-.33/4); % Fast dominant pulse + % increases clarity + end + end +end \ No newline at end of file