wolffd@0: function varargout = mirpeaks(orig,varargin) wolffd@0: % p = mirpeaks(x) detect peaks in x. wolffd@0: % Optional argument: wolffd@0: % mirpeaks(...,'Total',m): only the m highest peaks are selected. wolffd@0: % If m=Inf, no limitation of number of peaks. wolffd@0: % Default value: m = Inf wolffd@0: % mirpeaks(...,'Order',o): specifies the ordering of the peaks. wolffd@0: % Possible values for o: wolffd@0: % 'Amplitude': orders the peaks from highest to lowest wolffd@0: % (Default choice.) wolffd@0: % 'Abscissa': orders the peaks along the abscissa axis. wolffd@0: % mirpeaks(...,'Contrast',cthr): 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 x: a distance of 1, for instance, is wolffd@0: % equivalent to the distance between the maximum and the minimum wolffd@0: % of x. wolffd@0: % Default value: cthr = 0.1 wolffd@0: % mirpeaks(...,'SelectFirst',fthr): If the 'Contrast' selection has wolffd@0: % been chosen, this additional option specifies that when one wolffd@0: % peak has to be chosen out of two candidates, and if the wolffd@0: % difference of their amplitude is below the threshold fthr, wolffd@0: % then the most ancien one is selected. wolffd@0: % Option toggled off by default: wolffd@0: % Default value if toggled on: fthr = cthr/2 wolffd@0: % mirpeaks(...,'Threshold',thr): a threshold value. wolffd@0: % A given local maximum will be considered as a peak if its wolffd@0: % normalized amplitude is higher than this threshold. wolffd@0: % A given local minimum will be considered as a valley if its wolffd@0: % normalized amplitude is lower than this threshold. wolffd@0: % The normalized amplitude can have value between 0 (the minimum wolffd@0: % of the signal in each frame) and 1 (the maximum in each wolffd@0: % frame). wolffd@0: % Default value: thr=0 for peaks thr = 1 for valleys wolffd@0: % mirpeaks(...,'Interpol',i): estimates more precisely the peak wolffd@0: % position and amplitude using interpolation. Performed only on wolffd@0: % data with numerical abscissae axis. wolffd@0: % Possible value for i: wolffd@0: % '', 'no', 'off', 0: no interpolation wolffd@0: % 'Quadratic': quadratic interpolation. (default value). wolffd@0: % mirpeaks(...,'Valleys'): detect valleys instead of peaks. wolffd@0: % mirpeaks(...,'Reso',r): removes peaks whose distance to one or wolffd@0: % several higher peaks is lower than a given threshold. wolffd@0: % Possible value for the threshold r: wolffd@0: % 'SemiTone': ratio between the two peak positions equal to wolffd@0: % 2^(1/12) wolffd@0: % a numerical value : difference between the two peak wolffd@0: % positions equal to that value wolffd@0: % When two peaks are distant by an interval lower than the wolffd@0: % resolution, the highest of them is selected by default. wolffd@0: % mirpeaks(...,'Reso',r,'First') specifies on the contrary that wolffd@0: % the first of them is selected by default. wolffd@0: % mirpeaks(...,'Nearest',t,s): takes the peak nearest a given abscisse wolffd@0: % values t. The distance is computed either on a linear scale wolffd@0: % (s = 'Lin') or logarithmic scale (s = 'Log'). In this case, wolffd@0: % only one peak is extracted. wolffd@0: % mirpeaks(...,'Pref',c,std): indicates a region of preference for wolffd@0: % the peak picking, centered on the abscisse value c, with a wolffd@0: % standard deviation of std. wolffd@0: % mirpeaks(...,'NoBegin'): does not consider the first sample as a wolffd@0: % possible peak candidate. wolffd@0: % mirpeaks(...,'NoEnd'): does not consider the last sample as a possible wolffd@0: % peak candidate. wolffd@0: % mirpeaks(...,'Normalize',n): specifies whether frames are wolffd@0: % normalized globally or individually. wolffd@0: % Possible value for n: wolffd@0: % 'Global': normalizes the whole frames altogether from 0 to wolffd@0: % 1 (default choice). wolffd@0: % 'Local': normalizes each frame from 0 to 1. wolffd@0: % mirpeaks(...,'Extract'): extracts from the initial curves all the wolffd@0: % positive continuous segments (or "curve portions") where peaks wolffd@0: % are located. wolffd@0: % mirpeaks(...,'Only'): keeps from the original curve only the data wolffd@0: % corresponding to the peaks, and zeroes the remaining data. wolffd@0: % mirpeaks(...,'Track',t): tracks temporal continuities of peaks. If wolffd@0: % a value t is specified, the variation between successive peaks wolffd@0: % is tolerated up to t samples. wolffd@0: % mirpeaks(...,'CollapseTrack',ct): collapses tracks into one single wolffd@0: % track, and remove small track transitions, of length shorter wolffd@0: % than ct samples. Default value: ct = 7 wolffd@0: wolffd@0: m.key = 'Total'; wolffd@0: m.type = 'Integer'; wolffd@0: m.default = Inf; wolffd@0: option.m = m; wolffd@0: wolffd@0: nobegin.key = 'NoBegin'; wolffd@0: nobegin.type = 'Boolean'; wolffd@0: nobegin.default = 0; wolffd@0: option.nobegin = nobegin; wolffd@0: wolffd@0: noend.key = 'NoEnd'; wolffd@0: noend.type = 'Boolean'; wolffd@0: noend.default = 0; wolffd@0: option.noend = noend; wolffd@0: wolffd@0: order.key = 'Order'; wolffd@0: order.type = 'String'; wolffd@0: order.choice = {'Amplitude','Abscissa'}; wolffd@0: order.default = 'Amplitude'; wolffd@0: option.order = order; wolffd@0: wolffd@0: chro.key = 'Chrono'; % obsolete, corresponds to 'Order','Abscissa' wolffd@0: chro.type = 'Boolean'; wolffd@0: chro.default = 0; wolffd@0: option.chro = chro; wolffd@0: wolffd@0: ranked.key = 'Ranked'; % obsolete, corresponds to 'Order','Amplitude' wolffd@0: ranked.type = 'Boolean'; wolffd@0: ranked.default = 0; wolffd@0: option.ranked = ranked; wolffd@0: wolffd@0: vall.key = 'Valleys'; wolffd@0: vall.type = 'Boolean'; wolffd@0: vall.default = 0; wolffd@0: option.vall = vall; wolffd@0: wolffd@0: cthr.key = 'Contrast'; wolffd@0: cthr.type = 'Integer'; wolffd@0: cthr.default = .1; wolffd@0: option.cthr = cthr; wolffd@0: wolffd@0: first.key = 'SelectFirst'; wolffd@0: first.type = 'Integer'; wolffd@0: first.default = 0; wolffd@0: first.keydefault = NaN; wolffd@0: option.first = first; wolffd@0: wolffd@0: thr.key = 'Threshold'; wolffd@0: thr.type = 'Integer'; wolffd@0: thr.default = NaN; wolffd@0: option.thr = thr; wolffd@0: wolffd@0: smthr.key = 'MatrixThreshold'; % to be documented in version 1.3 wolffd@0: smthr.type = 'Integer'; wolffd@0: smthr.default = NaN; wolffd@0: option.smthr = smthr; wolffd@0: wolffd@0: graph.key = 'Graph'; wolffd@0: graph.type = 'Integer'; wolffd@0: graph.default = 0; wolffd@0: graph.keydefault = .25; wolffd@0: option.graph = graph; wolffd@0: wolffd@0: interpol.key = 'Interpol'; wolffd@0: interpol.type = 'String'; wolffd@0: interpol.default = 'Quadratic'; wolffd@0: interpol.keydefault = 'Quadratic'; wolffd@0: option.interpol = interpol; wolffd@0: wolffd@0: reso.key = 'Reso'; wolffd@0: %reso.type = 'String'; wolffd@0: %reso.choice = {0,'SemiTone'}; wolffd@0: reso.default = 0; wolffd@0: option.reso = reso; wolffd@0: wolffd@0: resofirst.key = 'First'; wolffd@0: resofirst.type = 'Boolean'; wolffd@0: resofirst.default = 0; wolffd@0: option.resofirst = resofirst; wolffd@0: wolffd@0: c.key = 'Pref'; wolffd@0: c.type = 'Integer'; wolffd@0: c.number = 2; wolffd@0: c.default = [0 0]; wolffd@0: option.c = c; wolffd@0: wolffd@0: near.key = 'Nearest'; wolffd@0: near.type = 'Integer'; wolffd@0: near.default = NaN; wolffd@0: option.near = near; wolffd@0: wolffd@0: logsc.type = 'String'; wolffd@0: logsc.choice = {'Lin','Log',0}; wolffd@0: logsc.default = 'Lin'; wolffd@0: option.logsc = logsc; wolffd@0: wolffd@0: normal.key = 'Normalize'; wolffd@0: normal.type = 'String'; wolffd@0: normal.choice = {'Local','Global'}; wolffd@0: normal.default = 'Global'; wolffd@0: option.normal = normal; wolffd@0: wolffd@0: extract.key = 'Extract'; wolffd@0: extract.type = 'Boolean'; wolffd@0: extract.default = 0; wolffd@0: option.extract = extract; wolffd@0: wolffd@0: only.key = 'Only'; wolffd@0: only.type = 'Boolean'; wolffd@0: only.default = 0; wolffd@0: option.only = only; wolffd@0: wolffd@0: delta.key = 'Track'; wolffd@0: delta.type = 'Integer'; wolffd@0: delta.default = 0; wolffd@0: delta.keydefault = Inf; wolffd@0: option.delta = delta; wolffd@0: wolffd@0: mem.key = 'TrackMem'; wolffd@0: mem.type = 'Integer'; wolffd@0: mem.default = Inf; wolffd@0: option.mem = mem; wolffd@0: wolffd@0: shorttrackthresh.key = 'CollapseTracks'; wolffd@0: shorttrackthresh.type = 'Integer'; wolffd@0: shorttrackthresh.default = 0; wolffd@0: shorttrackthresh.keydefault = 7; wolffd@0: option.shorttrackthresh = shorttrackthresh; wolffd@0: wolffd@0: scan.key = 'ScanForward'; % specific to mironsets(..., 'Klapuri99') wolffd@0: scan.default = []; wolffd@0: option.scan = scan; wolffd@0: wolffd@0: specif.option = option; wolffd@0: wolffd@0: varargout = mirfunction(@mirpeaks,orig,varargin,nargout,specif,@init,@main); wolffd@0: wolffd@0: wolffd@0: function [x type] = init(x,option) wolffd@0: type = mirtype(x); wolffd@0: wolffd@0: wolffd@0: function p = main(x,option,postoption) wolffd@0: if iscell(x) wolffd@0: x = x{1}; wolffd@0: end wolffd@0: if option.chro wolffd@0: option.order = 'Abscissa'; wolffd@0: elseif option.ranked wolffd@0: option.order = 'Amplitude'; wolffd@0: end wolffd@0: if not(isnan(option.near)) && option.m == 1 wolffd@0: option.m = Inf; wolffd@0: end wolffd@0: x = purgedata(x); wolffd@0: if option.m <= 0 wolffd@0: p = x; wolffd@0: return wolffd@0: end wolffd@0: d = get(x,'Data'); wolffd@0: sr = get(x,'Sampling'); wolffd@0: cha = 0; % Indicates when it is possible to represent as a curve along the wolffd@0: % Z-axis (channels) instead of the X-axis (initial abscissa). wolffd@0: if isnan(option.first) wolffd@0: option.first = option.cthr / 2; wolffd@0: end wolffd@0: if isa(x,'mirscalar') wolffd@0: t = get(x,'FramePos'); wolffd@0: for i = 1:length(d) wolffd@0: for j = 1:length(d{i}) wolffd@0: d{i}{j} = d{i}{j}'; wolffd@0: if size(t{i},1) == 1 wolffd@0: t{i}{j} = t{i}{j}(1,:,:)'; wolffd@0: else wolffd@0: t{i}{j} = (t{i}{j}(1,:,:)+t{i}{j}(2,:,:))'/2; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: elseif isa(x,'mirsimatrix') wolffd@0: t = get(x,'FramePos'); wolffd@0: for i = 1:length(t) wolffd@0: for j = 1:length(t{i}) wolffd@0: t{i}{j} = repmat((t{i}{j}(1,:,:)+t{i}{j}(2,:,:))'/2,... wolffd@0: [1 size(d{i}{j},2) 1]); wolffd@0: end wolffd@0: end wolffd@0: elseif isa(x,'mirhisto') wolffd@0: error('ERROR IN MIRPEAKS: peaks of histogram not considered yet.'); wolffd@0: else wolffd@0: for i = 1:length(d) wolffd@0: for j = 1:length(d{i}) wolffd@0: if iscell(d{i}) wolffd@0: dij = d{i}{j}; wolffd@0: if ~cha && j == 1 && size(dij,3) > 1 && size(dij,1) == 1 wolffd@0: cha = 1; wolffd@0: end wolffd@0: if cha && j > 1 && size(dij,1) > 1 wolffd@0: cha = -1; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: for j = 1:length(d{i}) wolffd@0: if iscell(d{i}) wolffd@0: dij = d{i}{j}; wolffd@0: if cha == 1 wolffd@0: if iscell(dij) wolffd@0: for k = 1:size(dij,2) wolffd@0: d{i}{j}{k} = reshape(dij{k},size(dij{k},2),size(dij{k},3)); wolffd@0: d{i}{j}{k} = d{i}{j}{k}'; wolffd@0: end wolffd@0: else wolffd@0: d{i}{j} = reshape(dij,size(dij,2),size(dij,3)); wolffd@0: d{i}{j} = d{i}{j}'; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if cha == 1 wolffd@0: t = get(x,'Channels'); wolffd@0: else wolffd@0: t = get(x,'Pos'); wolffd@0: end wolffd@0: end wolffd@0: pp = cell(1,length(d)); wolffd@0: pv = cell(1,length(d)); wolffd@0: pm = cell(1,length(d)); wolffd@0: ppp = cell(1,length(d)); wolffd@0: ppv = cell(1,length(d)); wolffd@0: tp = cell(1,length(d)); wolffd@0: tv = cell(1,length(d)); wolffd@0: wolffd@0: if isnan(option.thr) wolffd@0: option.thr = 0; wolffd@0: else wolffd@0: if option.vall wolffd@0: option.thr = 1-option.thr; wolffd@0: end wolffd@0: end wolffd@0: %if isnan(option.lthr) wolffd@0: % option.lthr = 1; wolffd@0: %else wolffd@0: % if option.vall wolffd@0: % option.lthr = 1-option.lthr; wolffd@0: % end wolffd@0: %end wolffd@0: if isnan(option.smthr) wolffd@0: option.smthr = option.thr - .2; wolffd@0: end wolffd@0: wolffd@0: if not(isempty(option.scan)) wolffd@0: pscan = get(option.scan,'PeakPos'); wolffd@0: end wolffd@0: wolffd@0: interpol = get(x,'Interpolable') && not(isempty(option.interpol)) && ... wolffd@0: ((isnumeric(option.interpol) && option.interpol) || ... wolffd@0: (ischar(option.interpol) && not(strcmpi(option.interpol,'No')) && not(strcmpi(option.interpol,'Off')))); wolffd@0: wolffd@0: for i = 1:length(d) % For each audio file,... wolffd@0: di = d{i}; wolffd@0: if cha == 1 wolffd@0: ti = t; %sure ? wolffd@0: else wolffd@0: ti = t{i}; wolffd@0: end wolffd@0: if not(iscell(di)) wolffd@0: di = {di}; wolffd@0: if isa(x,'mirchromagram') && not(cha) wolffd@0: ti = {ti}; wolffd@0: end wolffd@0: end wolffd@0: for h = 1:length(di) % For each segment,... wolffd@0: dh0 = di{h}; wolffd@0: if option.vall wolffd@0: dh0 = -dh0; wolffd@0: end wolffd@0: dh2 = dh0; wolffd@0: [nl0 nc np nd0] = size(dh0); wolffd@0: if cha == 1 wolffd@0: if iscell(ti) wolffd@0: %% problem here!!! wolffd@0: ti = ti{i}; %%%%%it seems that sometimes we need to use instead ti{i}(:); wolffd@0: end wolffd@0: th = repmat(ti,[1,nc,np,nd0]); wolffd@0: else wolffd@0: th = ti{h}; wolffd@0: if iscell(th) % Non-numerical abscissae are transformed into numerical ones. wolffd@0: th = repmat((1:size(th,1))',[1,nc,np]); wolffd@0: else wolffd@0: if size(th,3)= option.cthr, ... wolffd@0: dk >= option.thr),... wolffd@0: ... dk <= option.lthr)), wolffd@0: and(ddh(1:(end-1),k,l) > 0, ... wolffd@0: ddh(2:end,k,l) <= 0)))+1; wolffd@0: end wolffd@0: end wolffd@0: if option.cthr wolffd@0: for l = 1:np wolffd@0: for k = 1:nc wolffd@0: finalmxk = []; wolffd@0: mxk = mx{1,k,l}; wolffd@0: if not(isempty(mxk)) wolffd@0: wait = 0; wolffd@0: if length(mxk)>5000 wolffd@0: wait = waitbar(0,['Selecting peaks... (0 out of 0)']); wolffd@0: end wolffd@0: %if option.m < Inf wolffd@0: % [unused,idx] = sort(dh(mxk,k,l),'descend'); % The peaks are sorted in decreasing order wolffd@0: % mxk = mxk(sort(idx(1:option.m))); wolffd@0: %end wolffd@0: j = 1; % Scans the peaks from begin to end. wolffd@0: mxkj = mxk(j); % The current peak wolffd@0: jj = j+1; wolffd@0: bufmin = Inf; wolffd@0: bufmax = dh(mxkj,k,l); wolffd@0: oldbufmin = min(dh(1:mxk(1)-1,k,l)); wolffd@0: while jj <= length(mxk) wolffd@0: if wait && not(mod(jj,5000)) wolffd@0: waitbar(jj/length(mxk),wait,['Selecting peaks... (',num2str(length(finalmxk)),' out of ',num2str(jj),')']); wolffd@0: end wolffd@0: bufmin = min(bufmin, ... wolffd@0: min(dh(mxk(jj-1)+1:mxk(jj)-1,k,l))); wolffd@0: if bufmax - bufmin < option.cthr wolffd@0: % There is no contrastive notch wolffd@0: if dh(mxk(jj),k,l) > bufmax && ... wolffd@0: (dh(mxk(jj),k,l) - bufmax > option.first ... wolffd@0: || (bufmax - oldbufmin < option.cthr)) wolffd@0: % If the new peak is significantly wolffd@0: % higher than the previous one, wolffd@0: % The peak is transfered to the new wolffd@0: % position wolffd@0: j = jj; wolffd@0: mxkj = mxk(j); % The current peak wolffd@0: bufmax = dh(mxkj,k,l); wolffd@0: oldbufmin = min(oldbufmin,bufmin); wolffd@0: bufmin = Inf; wolffd@0: elseif dh(mxk(jj),k,l) - bufmax <= option.first wolffd@0: bufmax = max(bufmax,dh(mxk(jj),k,l)); wolffd@0: oldbufmin = min(oldbufmin,bufmin); wolffd@0: end wolffd@0: else wolffd@0: % There is a contrastive notch wolffd@0: if bufmax - oldbufmin < option.cthr wolffd@0: % But the previous peak candidate wolffd@0: % is too weak and therefore wolffd@0: % discarded wolffd@0: oldbufmin = min(oldbufmin,bufmin); wolffd@0: else wolffd@0: % The previous peak candidate is OK wolffd@0: % and therefore stored wolffd@0: finalmxk(end+1) = mxkj; wolffd@0: oldbufmin = bufmin; wolffd@0: end wolffd@0: bufmax = dh(mxk(jj),k,l); wolffd@0: j = jj; wolffd@0: mxkj = mxk(j); % The current peak wolffd@0: bufmin = Inf; wolffd@0: end wolffd@0: jj = jj+1; wolffd@0: end wolffd@0: if bufmax - oldbufmin >= option.cthr && ... wolffd@0: bufmax - min(dh(mxk(j)+1:end,k,l)) >= option.cthr wolffd@0: % The last peak candidate is OK and stored wolffd@0: finalmxk(end+1) = mxk(j); wolffd@0: end wolffd@0: if wait wolffd@0: waitbar(1,wait); wolffd@0: close(wait); wolffd@0: drawnow wolffd@0: end wolffd@0: end wolffd@0: mx{1,k,l} = finalmxk; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if not(isempty(option.scan)) % 'ScanForward' option, used for 'Klapuri99' in mironsets wolffd@0: for l = 1:np wolffd@0: for k = 1:nc wolffd@0: pscankl = pscan{i}{h}{1,k,l}; % scan seed positions wolffd@0: mxkl = []; wolffd@0: lp = length(pscankl); % number of peaks wolffd@0: for jj = 1:lp % for each peak wolffd@0: fmx = find(mx{1,k,l}>pscankl(jj),1); wolffd@0: % position of the next max following the wolffd@0: % current seed wolffd@0: fmx = mx{1,k,l}(fmx); wolffd@0: if jj=pscankl(jj+1)) wolffd@0: [unused fmx] = max(dh(pscankl(jj):... wolffd@0: pscankl(jj+1)-1,k,l)); wolffd@0: fmx = fmx+pscankl(jj)-1; wolffd@0: elseif jj==lp && isempty(fmx) wolffd@0: [unused fmx] = max(dh(pscankl(jj):end,k,l)); wolffd@0: fmx = fmx+pscankl(jj)-1; wolffd@0: end wolffd@0: mxkl = [mxkl fmx]; wolffd@0: end wolffd@0: mx{1,k,l} = mxkl; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if not(isequal(option.reso,0)) % Removing peaks too close to higher peaks wolffd@0: if ischar(option.reso) && strcmpi(option.reso,'SemiTone') wolffd@0: compar = @semitone_compar; wolffd@0: elseif isnumeric(option.reso) wolffd@0: compar = @dist_compar; wolffd@0: end wolffd@0: for l = 1:np wolffd@0: for k = 1:nc wolffd@0: mxlk = sort(mx{1,k,l}); wolffd@0: j = 1; wolffd@0: while j < length(mxlk)-1 wolffd@0: if compar(th(mxlk(j+1),k,l),th(mxlk(j),k,l),option.reso) wolffd@0: decreas = option.resofirst || ... wolffd@0: dh(mxlk(j+1),k,l)1 && compar(th(mxlk(end),k,l),... wolffd@0: th(mxlk(end-1),k,l),... wolffd@0: option.reso) wolffd@0: decreas = not(option.resofirst) &&... wolffd@0: dh(mxlk(end),k,l)>dh(mxlk(end-1),k,l); wolffd@0: mxlk(end-decreas) = []; wolffd@0: end wolffd@0: mx{1,k,l} = mxlk; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if not(isnan(option.near)) % Finding a peak nearest a given prefered location wolffd@0: for l = 1:np wolffd@0: for k = 1:nc wolffd@0: mxlk = mx{1,k,l}; wolffd@0: if strcmp(option.logsc,'Log') wolffd@0: [M I] = min(abs(log(th(mxlk,k,l)/option.near))); wolffd@0: else wolffd@0: [M I] = min(abs(th(mxlk,k,l)-option.near)); wolffd@0: end wolffd@0: mx{1,k,l} = mxlk(I); wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if option.delta % Peak tracking wolffd@0: tp{i}{h} = cell(1,np); wolffd@0: if interpol wolffd@0: tpp{i}{h} = cell(1,np); wolffd@0: tpv{i}{h} = cell(1,np); wolffd@0: end wolffd@0: for l = 1:np wolffd@0: wolffd@0: % mxl will be the resulting track position matrix wolffd@0: % and myl the related track amplitude wolffd@0: % In the first frame, tracks can be identified to peaks. wolffd@0: mxl = mx{1,1,l}(:)-1; wolffd@0: myl = dh(mx{1,1,l}(:),k,l); wolffd@0: wolffd@0: % To each peak is associated the related track ID wolffd@0: tr2 = 1:length(mx{1,1,l}); wolffd@0: wolffd@0: grvy = []; % The graveyard... wolffd@0: wolffd@0: wait = 0; wolffd@0: if nc-1>500 wolffd@0: wait = waitbar(0,['Tracking peaks...']); wolffd@0: end wolffd@0: wolffd@0: for k = 1:nc-1 wolffd@0: % For each successive frame... wolffd@0: wolffd@0: if not(isempty(grvy)) wolffd@0: old = find(grvy(:,2) == k-option.mem-1); wolffd@0: grvy(old,:) = []; wolffd@0: end wolffd@0: wolffd@0: if wait && not(mod(k,100)) wolffd@0: waitbar(k/(nc-1),wait); wolffd@0: end wolffd@0: wolffd@0: mxk1 = mx{1,k,l}; % w^k wolffd@0: mxk2 = mx{1,k+1,l}; % w^{k+1} wolffd@0: thk1 = th(mxk1,k,l); wolffd@0: thk2 = th(mxk2,k,l); wolffd@0: myk2 = dh(mx{1,k+1,l},k,l); % amplitude wolffd@0: tr1 = tr2; wolffd@0: tr2 = NaN(1,length(mxk2)); wolffd@0: wolffd@0: mxl(:,k+1) = mxl(:,k); wolffd@0: wolffd@0: if isempty(thk1) || isempty(thk2) wolffd@0: %% IS THIS TEST NECESSARY?? wolffd@0: wolffd@0: myl(:,k+1) = 0; wolffd@0: else wolffd@0: for n = 1:length(mxk1) wolffd@0: % Let's check each track. wolffd@0: tr = tr1(n); % Current track. wolffd@0: wolffd@0: if not(isnan(tr)) wolffd@0: % still alive... wolffd@0: wolffd@0: % Step 1 in Mc Aulay & Quatieri wolffd@0: [int m] = min(abs(thk2-thk1(n))); wolffd@0: if isinf(int) || int > option.delta wolffd@0: % all w^{k+1} outside matching interval: wolffd@0: % partial becomes dead wolffd@0: mxl(tr,k+1) = mxl(tr,k); wolffd@0: myl(tr,k+1) = 0; wolffd@0: grvy = [grvy; tr k]; % added to the graveyard wolffd@0: else wolffd@0: % closest w^{k+1} is tentatively selected: wolffd@0: % candidate match wolffd@0: wolffd@0: % Step 2 in Mc Aulay & Quatieri wolffd@0: [best mm] = min(abs(thk2(m)-th(mx{1,k,l}))); wolffd@0: if mm == n wolffd@0: % no better match to remaining w^k: wolffd@0: % definite match wolffd@0: mxl(tr,k+1) = mxk2(m)-1; wolffd@0: myl(tr,k+1) = myk2(m); wolffd@0: tr2(m) = tr; wolffd@0: thk1(n) = -Inf; % selected w^k is eliminated from further consideration wolffd@0: thk2(m) = Inf; % selected w^{k+1} is eliminated as well wolffd@0: if not(isempty(grvy)) wolffd@0: zz = find ((mxl(grvy(:,1),k) >= mxl(tr,k) & ... wolffd@0: mxl(grvy(:,1),k) <= mxl(tr,k+1)) | ... wolffd@0: (mxl(grvy(:,1),k) <= mxl(tr,k) & ... wolffd@0: mxl(grvy(:,1),k) >= mxl(tr,k+1))); wolffd@0: grvy(zz,:) = []; wolffd@0: end wolffd@0: else wolffd@0: % let's look at adjacent lower w^{k+1}... wolffd@0: [int mmm] = min(abs(thk2(1:m)-thk1(n))); wolffd@0: if int > best || ... % New condition added (Lartillot 16.4.2010) wolffd@0: isinf(int) || ... % Conditions proposed in Mc Aulay & Quatieri (all w^{k+1} below matching interval) wolffd@0: int > option.delta wolffd@0: % partial becomes dead wolffd@0: mxl(tr,k+1) = mxl(tr,k); wolffd@0: myl(tr,k+1) = 0; wolffd@0: grvy = [grvy; tr k]; % added to the graveyard wolffd@0: else wolffd@0: % definite match wolffd@0: mxl(tr,k+1) = mxk2(mmm)-1; wolffd@0: myl(tr,k+1) = myk2(mmm); wolffd@0: tr2(mmm) = tr; wolffd@0: thk1(n) = -Inf; % selected w^k is eliminated from further consideration wolffd@0: thk2(mmm) = Inf; % selected w^{k+1} is eliminated as well wolffd@0: if not(isempty(grvy)) wolffd@0: zz = find ((mxl(grvy(:,1),k) >= mxl(tr,k) & ... wolffd@0: mxl(grvy(:,1),k) <= mxl(tr,k+1)) | ... wolffd@0: (mxl(grvy(:,1),k) <= mxl(tr,k) & ... wolffd@0: mxl(grvy(:,1),k) >= mxl(tr,k+1))); wolffd@0: grvy(zz,:) = []; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: % Step 3 in Mc Aulay & Quatieri wolffd@0: for m = 1:length(mxk2) wolffd@0: if not(isinf(thk2(m))) wolffd@0: % unmatched w^{k+1} wolffd@0: wolffd@0: if isempty(grvy) wolffd@0: int = []; wolffd@0: else wolffd@0: % Let's try to reuse a zombie from the wolffd@0: % graveyard (Lartillot). wolffd@0: [int z] = min(abs(th(mxl(grvy(:,1),k+1)+1,k,l)-thk2(m))); wolffd@0: end wolffd@0: if isempty(int) || int > option.delta ... wolffd@0: || int > min(abs(th(mxl(:,k+1)+1,k,l)-thk2(m))) wolffd@0: % No suitable zombie. wolffd@0: % birth of a new partial (Mc Aulay & wolffd@0: % Quatieri) wolffd@0: mxl = [mxl;zeros(1,k+1)]; wolffd@0: tr = size(mxl,1); wolffd@0: mxl(tr,k) = mxk2(m)-1; wolffd@0: else wolffd@0: % Suitable zombie found. (Lartillot) wolffd@0: tr = grvy(z,1); wolffd@0: grvy(z,:) = []; wolffd@0: end wolffd@0: mxl(tr,k+1) = mxk2(m)-1; wolffd@0: myl(tr,k+1) = myk2(m); wolffd@0: tr2(m) = tr; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: if wait wolffd@0: waitbar(1,wait); wolffd@0: close(wait); wolffd@0: drawnow wolffd@0: end wolffd@0: wolffd@0: if size(mxl,1) > option.m wolffd@0: tot = sum(myl,2); wolffd@0: [tot ix] = sort(tot,'descend'); wolffd@0: mxl(ix(option.m+1:end),:) = []; wolffd@0: myl(ix(option.m+1:end),:) = []; wolffd@0: end wolffd@0: wolffd@0: mxl(:,not(max(myl))) = 0; wolffd@0: wolffd@0: if option.shorttrackthresh wolffd@0: [myl bestrack] = max(myl,[],1); wolffd@0: mxl = mxl(bestrack + (0:size(mxl,2)-1)*size(mxl,1)); wolffd@0: changes = find(not(bestrack(1:end-1) == bestrack(2:end)))+1; wolffd@0: if not(isempty(changes)) wolffd@0: lengths = diff([1 changes nc+1]); wolffd@0: shorts = find(lengths < option.shorttrackthresh); wolffd@0: for k = 1:length(shorts) wolffd@0: if shorts(k) == 1 wolffd@0: k1 = 1; wolffd@0: else wolffd@0: k1 = changes(shorts(k)-1); wolffd@0: end wolffd@0: k2 = k1 + lengths(shorts(k)) -1; wolffd@0: myl(1,k1:k2) = 0; wolffd@0: mxl(1,k1:k2) = 0; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: tp{i}{h}{l} = mxl; wolffd@0: tv{i}{h}{l} = myl; wolffd@0: wolffd@0: if interpol wolffd@0: tpv{i}{h}{l} = zeros(size(mxl)); wolffd@0: tpp{i}{h}{l} = zeros(size(mxl)); wolffd@0: for k = 1:size(mxl,2) wolffd@0: for j = 1:size(mxl,1) wolffd@0: mj = mxl(j,k); wolffd@0: if mj>2 && mj= 0 wolffd@0: tpp{i}{h}{l}(j,k) = (1-p)*th(mj,k,l)+p*th(mj+1,k,l); wolffd@0: elseif p < 0 wolffd@0: tpp{i}{h}{l}(j,k) = (1+p)*th(mj,k,l)-p*th(mj-1,k,l); wolffd@0: end wolffd@0: elseif mj wolffd@0: tpv{i}{h}{l}(j,k) = dh3(mj,k,l); wolffd@0: tpp{i}{h}{l}(j,k) = th(mj,k,l); wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if isa(x,'mirsimatrix') && option.graph wolffd@0: % Finding the best branch inside a graph constructed out of a wolffd@0: % similarity matrix wolffd@0: g{i}{h} = cell(1,nc,np); wolffd@0: % Branch info related to each peak wolffd@0: br{i}{h} = {}; wolffd@0: % Info related to each branch wolffd@0: scog{i}{h} = cell(1,nc,np); wolffd@0: % Score related to each peak wolffd@0: scob{i}{h} = []; wolffd@0: % Score related to each branch wolffd@0: for l = 1:np wolffd@0: wait = waitbar(0,['Creating peaks graph...']); wolffd@0: for k = 1:nc wolffd@0: g{i}{h}{1,k,l} = cell(size(mx{1,k,l})); wolffd@0: scog{i}{h}{1,k,l} = zeros(size(mx{1,k,l})); wolffd@0: if wait && not(mod(k,50)) wolffd@0: waitbar(k/(nc-1),wait); wolffd@0: end wolffd@0: mxk = mx{1,k,l}; % Peaks in current frame wolffd@0: for j = k-1:-1:max(1,k-100) % Recent frames wolffd@0: mxj = mx{1,j,l}; % Peaks in one recent frame wolffd@0: for kk = 1:length(mxk) wolffd@0: mxkk = mxk(kk); % For each of current peaks wolffd@0: for jj = 1:length(mxj) wolffd@0: mxjj = mxj(jj); % For each of recent peaks wolffd@0: sco = k-j - abs(mxkk-mxjj); wolffd@0: % Crossprogression from recent to wolffd@0: % current peak wolffd@0: if sco >= 0 wolffd@0: % Negative crossprogression excluded wolffd@0: dist = 0; wolffd@0: % The distance between recent and wolffd@0: % current peak is the sum of all the wolffd@0: % simatrix values when joining the two wolffd@0: % peaks with a straight line. wolffd@0: for m = j:k wolffd@0: % Each point in that straight line. wolffd@0: mxm = mxjj + (mxkk-mxjj)*(m-j)/(k-j); wolffd@0: if mxm == floor(mxm) wolffd@0: dist = dist + 1-dh(mxm,m,l); wolffd@0: else wolffd@0: dhm0 = dh(floor(mxm),m,l); wolffd@0: dhm1 = dh(ceil(mxm),m,l); wolffd@0: dist = dist + 1-... wolffd@0: (dhm0 + ... wolffd@0: (dhm1-dhm0)*(mxm-floor(mxm))); wolffd@0: end wolffd@0: if dist > option.graph wolffd@0: break wolffd@0: end wolffd@0: end wolffd@0: if dist < option.graph wolffd@0: % If the distance between recent wolffd@0: % and current peak is not too high, wolffd@0: % a new edge is formed between the wolffd@0: % peaks, and added to the graph. wolffd@0: gj = g{i}{h}{1,j,l}{jj}; wolffd@0: % Branch information associated wolffd@0: % with recent peak wolffd@0: gk = g{i}{h}{1,k,l}{kk}; wolffd@0: % Branch information associated wolffd@0: % with current peak wolffd@0: if isempty(gk) || ... wolffd@0: sco > scog{i}{h}{1,k,l}(kk) wolffd@0: % Current peak branch to be updated wolffd@0: if isempty(gj) wolffd@0: % New branch starting wolffd@0: % from scratch wolffd@0: newsco = sco; wolffd@0: scob{i}{h}(end+1) = newsco; wolffd@0: bid = length(scob{i}{h}); wolffd@0: g{i}{h}{1,j,l}{jj} = ... wolffd@0: [k kk bid newsco]; wolffd@0: br{i}{h}{bid} = [j jj;k kk]; wolffd@0: else wolffd@0: newsco = scog{i}{h}{1,j,l}(jj)+sco; wolffd@0: if length(gj) == 1 wolffd@0: % Recent peak not wolffd@0: % associated with other wolffd@0: % branch wolffd@0: % -> Branch extension wolffd@0: bid = gj; wolffd@0: g{i}{h}{1,j,l}{jj} = ... wolffd@0: [k kk bid newsco]; wolffd@0: br{i}{h}{bid}(end+1,:) = [k kk]; wolffd@0: else wolffd@0: % Recent peak already wolffd@0: % associated with other wolffd@0: % branch wolffd@0: % -> Branch fusion wolffd@0: bid = length(scob{i}{h})+1; wolffd@0: g{i}{h}{1,j,l}{jj} = ... wolffd@0: [k kk bid newsco; gj]; wolffd@0: other = br{i}{h}{gj(1,3)}; wolffd@0: % Other branch wolffd@0: % info wolffd@0: % Let's copy its wolffd@0: % prefix to the new wolffd@0: % branch: wolffd@0: other(other(:,1)>j,:) = []; wolffd@0: br{i}{h}{bid} = [other;k kk]; wolffd@0: end wolffd@0: scob{i}{h}(bid) = newsco; wolffd@0: end wolffd@0: g{i}{h}{1,k,l}{kk} = bid; wolffd@0: % New peak associated with wolffd@0: % branch wolffd@0: scog{i}{h}{1,k,l}(kk) = newsco; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: [scob{i}{h} IX] = sort(scob{i}{h},'descend'); wolffd@0: % Branch are ordered from best score to lowest wolffd@0: br{i}{h} = br{i}{h}(IX); wolffd@0: if wait wolffd@0: waitbar(1,wait); wolffd@0: close(wait); wolffd@0: drawnow wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: for l = 1:np % Orders the peaks and select the best ones wolffd@0: for k = 1:nc wolffd@0: mxk = mx{1,k,l}; wolffd@0: if length(mxk) > option.m wolffd@0: [unused,idx] = sort(dh(mxk,k,l),'descend'); wolffd@0: idx = idx(1:option.m); wolffd@0: elseif strcmpi(option.order,'Amplitude') wolffd@0: [unused,idx] = sort(dh(mxk,k,l),'descend'); wolffd@0: else wolffd@0: idx = 1:length(dh(mxk,k,l)); wolffd@0: end wolffd@0: if strcmpi(option.order,'Abscissa') wolffd@0: mx{1,k,l} = sort(mxk(idx)); wolffd@0: elseif strcmpi(option.order,'Amplitude') wolffd@0: mx{1,k,l} = mxk(idx); wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if option.extract % Extracting the positive part of the curve containing the peaks wolffd@0: if isa(x,'mirtemporal') wolffd@0: filn = floor(sr{i}/25); wolffd@0: else wolffd@0: filn = 10; wolffd@0: end wolffd@0: if filn>1 && size(dh3,1)>5 wolffd@0: filn = min(filn,floor(size(dh3,1)/3)); wolffd@0: fild = filtfilt(ones(1,filn)/2,1,dh3(2:end-1,:,:))/filn/2; wolffd@0: else wolffd@0: fild = dh3(2:end-1,:,:); wolffd@0: end wolffd@0: fild = [zeros(1,size(fild,2),size(fild,3));diff(fild)]; wolffd@0: for l = 1:np wolffd@0: for k = 1:nc wolffd@0: idx = 1:size(dh,1); wolffd@0: mxlk = sort(mx{1,k,l}-1); wolffd@0: for j = 1:length(mxlk) wolffd@0: wolffd@0: if fild(mxlk(j),k,l) < 0 wolffd@0: bef0 = find(fild(1:mxlk(j)-1,k,l)>=0); wolffd@0: if isempty(bef0) wolffd@0: bef0 = []; wolffd@0: end wolffd@0: else wolffd@0: bef0 = mxlk(j)-1; wolffd@0: end wolffd@0: if isempty(bef0) wolffd@0: bef = 0; wolffd@0: else wolffd@0: bef = find(fild(1:bef0(end),k,l)<=0); wolffd@0: if isempty(bef) wolffd@0: bef = 0; wolffd@0: end wolffd@0: end wolffd@0: if j>1 && bef(end) 0 wolffd@0: aft0 = find(fild(mxlk(j)+1:end,k,l)<=0)+mxlk(j); wolffd@0: if isempty(aft0) wolffd@0: aft0 = []; wolffd@0: end wolffd@0: else wolffd@0: aft0 = mxlk(j)+1; wolffd@0: end wolffd@0: if isempty(aft0) wolffd@0: aft = size(d{i}{h},1)+1; wolffd@0: else wolffd@0: aft = find(fild(aft0(1):end,k,l)>=0)+mxlk(j); wolffd@0: if isempty(aft) wolffd@0: aft = size(d{i}{h},1)+1; wolffd@0: end wolffd@0: end wolffd@0: wolffd@0: idx(bef(end)+3:aft(1)-3) = 0; wolffd@0: end wolffd@0: idx = idx(find(idx)); wolffd@0: dh3(idx,k,l) = NaN; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: if option.vall wolffd@0: dh3 = -dh3; wolffd@0: end wolffd@0: mmx = cell(1,nc,np); wolffd@0: mmy = cell(1,nc,np); wolffd@0: mmv = cell(1,nc,np); wolffd@0: for l = 1:np wolffd@0: for k = 1:nc wolffd@0: mmx{1,k,l} = mod(mx{1,k,l}(:,:,1),nl0)-1; wolffd@0: mmy{1,k,l} = ceil(mx{1,k,l}/nl0); wolffd@0: mmv{1,k,l} = dh3(mx{1,k,l}(:,:,1),k,l); wolffd@0: end wolffd@0: end wolffd@0: pp{i}{h} = mmx; wolffd@0: pm{i}{h} = mmy; wolffd@0: pv{i}{h} = mmv; wolffd@0: if not(interpol) wolffd@0: ppp{i}{h} = {}; wolffd@0: ppv{i}{h} = {}; wolffd@0: else % Interpolate to find the more exact peak positions wolffd@0: pih = cell(1,nc,np); wolffd@0: vih = cell(1,nc,np); wolffd@0: for l = 1:np wolffd@0: for k = 1:nc wolffd@0: mxlk = mx{1,k,l}; wolffd@0: vih{1,k,l} = zeros(length(mxlk),1); wolffd@0: pih{1,k,l} = zeros(length(mxlk),1); wolffd@0: for j = 1:length(mxlk) wolffd@0: mj = mxlk(j); % Current values wolffd@0: if strcmpi(option.interpol,'quadratic') wolffd@0: if mj>2 && mj= 0 wolffd@0: pih{1,k,l}(j) = (1-p)*th(mj,k,l)+p*th(mj+1,k,l); wolffd@0: elseif p < 0 wolffd@0: pih{1,k,l}(j) = (1+p)*th(mj,k,l)-p*th(mj-1,k,l); wolffd@0: end wolffd@0: else wolffd@0: vih{1,k,l}(j) = dh3(mj,k,l); wolffd@0: pih{1,k,l}(j) = th(mj,k,l); wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: ppp{i}{h} = pih; wolffd@0: ppv{i}{h} = vih; wolffd@0: end wolffd@0: if not(iscell(d{i})) % for chromagram wolffd@0: d{i} = dh3(2:end-1,:,:,:); wolffd@0: else wolffd@0: if cha == 1 wolffd@0: d{i}{h} = zeros(1,size(dh3,2),size(dh3,1)-2); wolffd@0: for k = 1:size(dh3,2) wolffd@0: d{i}{h}(1,k,:) = dh3(2:end-1,k); wolffd@0: end wolffd@0: else wolffd@0: d{i}{h} = dh3(2:end-1,:,:,:); wolffd@0: end wolffd@0: end wolffd@0: if option.only wolffd@0: dih = zeros(size(d{i}{h})); wolffd@0: for l = 1:np wolffd@0: for k = 1:nc wolffd@0: dih(pp{i}{h}{1,k,l},k,l) = ... wolffd@0: d{i}{h}(pp{i}{h}{1,k,l},k,l); wolffd@0: end wolffd@0: end wolffd@0: d{i}{h} = dih; wolffd@0: end wolffd@0: end wolffd@0: end wolffd@0: p = set(x,'PeakPos',pp,'PeakVal',pv,'PeakMode',pm); wolffd@0: if interpol wolffd@0: p = set(p,'PeakPrecisePos',ppp,'PeakPreciseVal',ppv); wolffd@0: end wolffd@0: if option.extract wolffd@0: p = set(p,'Data',d); wolffd@0: end wolffd@0: empty = cell(1,length(d)); wolffd@0: if option.only wolffd@0: p = set(p,'Data',d,'PeakPos',empty,'PeakVal',empty,'PeakMode',empty); wolffd@0: end wolffd@0: if option.delta wolffd@0: p = set(p,'TrackPos',tp,'TrackVal',tv); wolffd@0: if interpol wolffd@0: p = set(p,'TrackPrecisePos',tpp,'TrackPreciseVal',tpv); wolffd@0: end wolffd@0: end wolffd@0: if isa(x,'mirsimatrix') && option.graph wolffd@0: p = set(p,'Graph',g,'Branch',br); wolffd@0: end wolffd@0: wolffd@0: wolffd@0: function y = semitone_compar(p1,p2,thres) wolffd@0: y = p1/p2 < 2^(1/12); wolffd@0: wolffd@0: wolffd@0: function y = dist_compar(p1,p2,thres) wolffd@0: y = p1-p2 < thres;