Code covered by the BSD License  

Highlights from
MIRtoolbox

image thumbnail
from MIRtoolbox by Olivier Lartillot
An innovative environment, on top of Matlab, for music and audio analysis

mirpulseclarity(orig,varargin)
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 = 'Integer';
            mu.default = 100;
        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 = 'Klapuri'; % 'Gammatone' not in Matlab Central version
                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

Contact us at files@mathworks.com