Daniel@0: function varargout = mironsets(x,varargin) Daniel@0: % o = mironsets(x) shows a temporal curve where peaks relate to the Daniel@0: % position of note onset times, and estimates those note onset Daniel@0: % positions. Daniel@0: % Optional arguments: Daniel@0: % mironsets(...,f) selects the strategy for the computation of the Daniel@0: % onset detection function. Daniel@0: % f = 'Envelope': Envelope of the audio signal. (Default choice). Daniel@0: % With two methods for envelope extraction: Daniel@0: % mironsets(...,'Spectro') (Default): Daniel@0: % mironsets(...,'SpectroFrame',fl,fh) species the frame Daniel@0: % length fl (in s.) and the hop factor fh (as a value Daniel@0: % between 0 and 1) Daniel@0: % Default values: fl = .1 s., fh = .1 Daniel@0: % the frequency reassigment method can be specified: Daniel@0: % 'Freq' (default), 'Mel', 'Bark' or 'Cents' (cf. mirspectrum). Daniel@0: % mironsets(...,'Filter'): Daniel@0: % mironsets(...,'Filterbank',nc) specifies a preliminary Daniel@0: % filterbank decomposition into nc channels. If nc = 0, Daniel@0: % no decomposition is performed. Daniel@0: % Default value: 40. Daniel@0: % mironsets(...,'FilterbankType',ft) specifies the type of Daniel@0: % filterbank (see mirfilterbank). Daniel@0: % Default value: 'Gammatone'; Daniel@0: % Options associated to the mirenvelope function can be Daniel@0: % passed here as well (see help mirenvelope): Daniel@0: % 'FilterType','Tau','PreDecim' Daniel@0: % mironsets(...,'Sum','no') does not sum back the channels at Daniel@0: % the end of the computation. The resulting onset curve Daniel@0: % remains therefore decomposed into several channels. Daniel@0: % Options associated to the mirenvelope function can be Daniel@0: % passed here as well (see help mirenvelope): Daniel@0: % 'HalfwaveCenter','Diff','HalfwaveDiff','Center', Daniel@0: % 'Smooth', 'Sampling','Log','Power','Lambda', Daniel@0: % ,'PostDecim','UpSample' Daniel@0: % f = 'SpectralFlux': Spectral flux of the audio signal. Daniel@0: % Options associated to the mirflux function can be Daniel@0: % passed here as well (see help mirflux): Daniel@0: % 'Inc' (toggled on by default here), Daniel@0: % 'Halfwave' (toggled on by default here), Daniel@0: % 'Complex' (toggled off by default), Daniel@0: % 'Median' (toggled on by default here) Daniel@0: % f = 'Pitch ':computes a frame-decomposed autocorrelation function , Daniel@0: % of same default characteristics than those returned Daniel@0: % by mirpitch, with however a range of frequencies set by Daniel@0: % the following options: Daniel@0: % 'Min' (set by default to 30 Hz), Daniel@0: % 'Max' (set by default to 1000 Hz), Daniel@0: % and subsequently computes the novelty curve of the Daniel@0: % resulting similatrix matrix. Daniel@0: % Option associated to the mirnovelty function can be Daniel@0: % passed here as well (see help mirnovelty): Daniel@0: % 'KernelSize' (set by default to 32 samples) Daniel@0: % mironsets(...,'Detect',d) toggles on or off the onset detection, Daniel@0: % which is based on the onset detection function. Daniel@0: % (By default toggled on.) Daniel@0: % Option associated to the mirpeaks function can be specified as Daniel@0: % well: Daniel@0: % 'Contrast' with default value c = .01 Daniel@0: % 'Threshold' with default value t = 0 Daniel@0: % mironsets(...,'Attack') (or 'Attacks') detects attack phases. Daniel@0: % mironsets(...,'Release') (or 'Releases') detects release phases. Daniel@0: % mironsets(...,'Gauss',o) estimate the attack and/or release Daniel@0: % points using a gaussian envelope smoothing of order o of the Daniel@0: % onset curve. Daniel@0: % mironsets(...,'Frame',...) decomposes into frames, with default frame Daniel@0: % length 3 seconds and hop factor .1 Daniel@0: % Preselected onset detection models: Daniel@0: % mironsets(...,'Scheirer') corresponds to (Scheirer, 1998): Daniel@0: % mironsets(...,'FilterBankType','Scheirer',... Daniel@0: % 'FilterType','HalfHann','Sampling',200,... Daniel@0: % 'HalfWaveDiff','Sum',0,'Detect',0) Daniel@0: % mironsets(...,'Klapuri99') corresponds to most of (Klapuri, 1999). Daniel@0: Daniel@0: %% options related to 'Envelope': Daniel@0: Daniel@0: env.key = 'Envelope'; Daniel@0: env.type = 'Boolean'; Daniel@0: env.default = NaN; Daniel@0: option.env = env; Daniel@0: Daniel@0: envmethod.key = 'Method'; % optional Daniel@0: envmethod.type = 'Boolean'; Daniel@0: option.envmethod = envmethod; Daniel@0: Daniel@0: envmeth.type = 'String'; Daniel@0: envmeth.choice = {'Filter','Spectro'}; Daniel@0: envmeth.default = 'Spectro'; Daniel@0: option.envmeth = envmeth; Daniel@0: Daniel@0: %% options related to 'Filter': Daniel@0: Daniel@0: filter.key = 'FilterType'; Daniel@0: filter.type = 'String'; Daniel@0: filter.choice = {'IIR','HalfHann'}; Daniel@0: filter.default = 'IIR'; Daniel@0: option.filter = filter; Daniel@0: Daniel@0: tau.key = 'Tau'; Daniel@0: tau.type = 'Integer'; Daniel@0: tau.default = .02; Daniel@0: option.tau = tau; Daniel@0: Daniel@0: fb.key = {'Filterbank','NbChannels'}; Daniel@0: fb.type = 'Integer'; Daniel@0: fb.default = 40; Daniel@0: option.fb = fb; Daniel@0: Daniel@0: filtertype.key = 'FilterbankType'; Daniel@0: filtertype.type = 'String'; Daniel@0: %filtertype.choice = {'Gammatone','2Channels','Scheirer','Klapuri'}; Daniel@0: filtertype.default = 'Gammatone'; Daniel@0: option.filtertype = filtertype; Daniel@0: Daniel@0: decim.key = {'Decim','PreDecim'}; Daniel@0: decim.type = 'Integer'; Daniel@0: decim.default = 0; Daniel@0: option.decim = decim; Daniel@0: Daniel@0: %% options related to 'Spectro': Daniel@0: Daniel@0: band.type = 'String'; Daniel@0: band.choice = {'Freq','Mel','Bark','Cents'}; Daniel@0: band.default = 'Freq'; Daniel@0: option.band = band; Daniel@0: Daniel@0: specframe.key = 'SpectroFrame'; Daniel@0: specframe.type = 'Integer'; Daniel@0: specframe.number = 2; Daniel@0: specframe.default = [.1 .1]; Daniel@0: option.specframe = specframe; Daniel@0: Daniel@0: sum.key = 'Sum'; Daniel@0: sum.type = 'Boolean'; Daniel@0: sum.default = 1; Daniel@0: option.sum = sum; Daniel@0: Daniel@0: chwr.key = 'HalfwaveCenter'; Daniel@0: chwr.type = 'Boolean'; Daniel@0: chwr.default = 0; Daniel@0: chwr.when = 'After'; Daniel@0: option.chwr = chwr; Daniel@0: Daniel@0: mu.key = 'Mu'; Daniel@0: mu.type = 'Boolean'; Daniel@0: mu.default = 0; Daniel@0: mu.when = 'After'; Daniel@0: option.mu = mu; Daniel@0: Daniel@0: oplog.key = 'Log'; Daniel@0: oplog.type = 'Boolean'; Daniel@0: oplog.default = 0; Daniel@0: oplog.when = 'After'; Daniel@0: option.log = oplog; Daniel@0: Daniel@0: oppow.key = 'Power'; Daniel@0: oppow.type = 'Boolean'; Daniel@0: oppow.default = 0; Daniel@0: oppow.when = 'After'; Daniel@0: option.power = oppow; Daniel@0: Daniel@0: diffenv.key = 'DiffEnvelope'; % obsolete, replaced by 'Diff' Daniel@0: diffenv.type = 'Boolean'; Daniel@0: diffenv.default = 0; Daniel@0: option.diffenv = diffenv; Daniel@0: Daniel@0: diff.key = 'Diff'; Daniel@0: diff.type = 'Integer'; Daniel@0: diff.default = 0; Daniel@0: diff.keydefault = 1; Daniel@0: diff.when = 'After'; Daniel@0: option.diff = diff; Daniel@0: Daniel@0: diffhwr.key = 'HalfwaveDiff'; Daniel@0: diffhwr.type = 'Integer'; Daniel@0: diffhwr.default = 0; Daniel@0: diffhwr.keydefault = 1; Daniel@0: diffhwr.when = 'After'; Daniel@0: option.diffhwr = diffhwr; Daniel@0: Daniel@0: lambda.key = 'Lambda'; Daniel@0: lambda.type = 'Integer'; Daniel@0: lambda.default = 1; Daniel@0: lambda.when = 'After'; Daniel@0: option.lambda = lambda; Daniel@0: Daniel@0: c.key = 'Center'; Daniel@0: c.type = 'Boolean'; Daniel@0: c.default = 0; Daniel@0: c.when = 'After'; Daniel@0: option.c = c; Daniel@0: Daniel@0: aver.key = 'Smooth'; Daniel@0: aver.type = 'Integer'; Daniel@0: aver.default = 0; Daniel@0: aver.keydefault = 30; Daniel@0: aver.when = 'After'; Daniel@0: option.aver = aver; Daniel@0: Daniel@0: ds.key = {'Down','PostDecim'}; Daniel@0: ds.type = 'Integer'; Daniel@0: if isamir(x,'mirenvelope') Daniel@0: ds.default = 1; Daniel@0: else Daniel@0: ds.default = NaN; Daniel@0: end Daniel@0: ds.when = 'After'; Daniel@0: ds.chunkcombine = 'During'; Daniel@0: option.ds = ds; Daniel@0: Daniel@0: sampling.key = 'Sampling'; Daniel@0: sampling.type = 'Integer'; Daniel@0: sampling.default = 0; Daniel@0: sampling.when = 'After'; Daniel@0: option.sampling = sampling; Daniel@0: Daniel@0: up.key = {'UpSample'}; Daniel@0: up.type = 'Integer'; Daniel@0: up.default = 0; Daniel@0: up.keydefault = 2; Daniel@0: option.up = up; Daniel@0: Daniel@0: %% options related to 'SpectralFlux' Daniel@0: flux.key = 'SpectralFlux'; Daniel@0: flux.type = 'Boolean'; Daniel@0: flux.default = 0; Daniel@0: option.flux = flux; Daniel@0: Daniel@0: complex.key = 'Complex'; Daniel@0: complex.type = 'Boolean'; Daniel@0: complex.when = 'Both'; Daniel@0: complex.default = 0; Daniel@0: option.complex = complex; Daniel@0: Daniel@0: inc.key = 'Inc'; Daniel@0: inc.type = 'Boolean'; Daniel@0: inc.default = 1; Daniel@0: option.inc = inc; Daniel@0: Daniel@0: median.key = 'Median'; Daniel@0: median.type = 'Integer'; Daniel@0: median.number = 2; Daniel@0: median.default = [.2 1.3]; Daniel@0: median.when = 'After'; Daniel@0: option.median = median; Daniel@0: Daniel@0: hw.key = 'Halfwave'; Daniel@0: hw.type = 'Boolean'; Daniel@0: hw.default = 1; Daniel@0: hw.when = 'After'; Daniel@0: option.hw = hw; Daniel@0: Daniel@0: %% options related to 'Pitch': Daniel@0: pitch.key = 'Pitch'; Daniel@0: pitch.type = 'Boolean'; Daniel@0: pitch.default = 0; Daniel@0: option.pitch = pitch; Daniel@0: Daniel@0: min.key = 'Min'; Daniel@0: min.type = 'Integer'; Daniel@0: min.default = 30; Daniel@0: option.min = min; Daniel@0: Daniel@0: max.key = 'Max'; Daniel@0: max.type = 'Integer'; Daniel@0: max.default = 1000; Daniel@0: option.max = max; Daniel@0: Daniel@0: kernelsize.key = 'KernelSize'; Daniel@0: kernelsize.type = 'Integer'; Daniel@0: kernelsize.default = 32; Daniel@0: option.kernelsize = kernelsize; Daniel@0: Daniel@0: %% options related to event detection Daniel@0: detect.key = 'Detect'; Daniel@0: detect.type = 'String'; Daniel@0: detect.choice = {'Peaks','Valleys',0,'no','off'}; Daniel@0: detect.default = 'Peaks'; Daniel@0: detect.keydefault = 'Peaks'; Daniel@0: detect.when = 'After'; Daniel@0: option.detect = detect; Daniel@0: Daniel@0: cthr.key = 'Contrast'; Daniel@0: cthr.type = 'Integer'; Daniel@0: cthr.default = NaN; Daniel@0: cthr.when = 'After'; Daniel@0: option.cthr = cthr; Daniel@0: Daniel@0: thr.key = 'Threshold'; Daniel@0: thr.type = 'Integer'; Daniel@0: thr.default = 0; Daniel@0: thr.when = 'After'; Daniel@0: option.thr = thr; Daniel@0: Daniel@0: attack.key = {'Attack','Attacks'}; Daniel@0: attack.type = 'Boolean'; Daniel@0: attack.default = 0; Daniel@0: attack.when = 'After'; Daniel@0: option.attack = attack; Daniel@0: Daniel@0: release.key = {'Release','Releases'}; Daniel@0: release.type = 'String'; Daniel@0: release.choice = {'Olivier','Valeri',0,'no','off'}; Daniel@0: release.default = 0; Daniel@0: release.keydefault = 'Olivier'; Daniel@0: release.when = 'After'; Daniel@0: option.release = release; Daniel@0: Daniel@0: gauss.key = 'Gauss'; Daniel@0: gauss.type = 'Integer'; Daniel@0: gauss.default = 0; Daniel@0: gauss.when = 'After'; Daniel@0: option.gauss = gauss; Daniel@0: Daniel@0: %% preselection Daniel@0: presel.choice = {'Scheirer','Klapuri99'}; Daniel@0: presel.type = 'String'; Daniel@0: presel.default = 0; Daniel@0: option.presel = presel; Daniel@0: Daniel@0: Daniel@0: %% 'Frame' option Daniel@0: frame.key = 'Frame'; Daniel@0: frame.type = 'Integer'; Daniel@0: frame.when = 'Both'; Daniel@0: frame.number = 2; Daniel@0: frame.default = [0 0]; Daniel@0: frame.keydefault = [3 .1]; Daniel@0: option.frame = frame; Daniel@0: Daniel@0: specif.option = option; Daniel@0: Daniel@0: specif.eachchunk = 'Normal'; Daniel@0: specif.combinechunk = 'Concat'; Daniel@0: specif.extensive = 1; Daniel@0: Daniel@0: specif.title = 'Onset curve'; %used for miroptions Daniel@0: Daniel@0: varargout = mirfunction(@mironsets,x,varargin,nargout,specif,@init,@main); Daniel@0: Daniel@0: Daniel@0: %% INIT Daniel@0: Daniel@0: function [y type] = init(x,option) Daniel@0: if iscell(x) Daniel@0: x = x{1}; Daniel@0: end Daniel@0: if ischar(option.presel) Daniel@0: if strcmpi(option.presel,'Scheirer') Daniel@0: option.filtertype = 'Scheirer'; Daniel@0: option.filter = 'HalfHann'; Daniel@0: option.envmeth = 'Filter'; Daniel@0: elseif strcmpi(option.presel,'Klapuri99') Daniel@0: option.filtertype = 'Klapuri'; Daniel@0: option.filter = 'HalfHann'; Daniel@0: option.envmeth = 'Filter'; Daniel@0: option.decim = 180; Daniel@0: end Daniel@0: end Daniel@0: if option.diffenv Daniel@0: option.env = 1; Daniel@0: end Daniel@0: if isnan(option.env) Daniel@0: if option.flux || option.pitch Daniel@0: option.env = 0; Daniel@0: else Daniel@0: option.env = 1; Daniel@0: end Daniel@0: end Daniel@0: if isamir(x,'miraudio') Daniel@0: if option.env Daniel@0: if strcmpi(option.envmeth,'Filter') && option.fb>1 Daniel@0: fb = mirfilterbank(x,option.filtertype,'NbChannels',option.fb); Daniel@0: else Daniel@0: fb = x; Daniel@0: end Daniel@0: y = mirenvelope(fb,option.envmeth,option.band,... Daniel@0: 'Frame',option.specframe(1),option.specframe(2),... Daniel@0: 'FilterType',option.filter,... Daniel@0: 'Tau',option.tau,'UpSample',option.up,... Daniel@0: 'PreDecim',option.decim,'PostDecim',0); Daniel@0: type = 'mirenvelope'; Daniel@0: elseif option.flux Daniel@0: x = mirframenow(x,option); Daniel@0: y = mirflux(x,'Inc',option.inc,'Complex',option.complex); Daniel@0: type = 'mirscalar'; Daniel@0: elseif option.pitch Daniel@0: [unused ac] = mirpitch(x,'Frame','Min',option.min,'Max',option.max); Daniel@0: y = mirnovelty(ac,'KernelSize',option.kernelsize); Daniel@0: type = 'mirscalar'; Daniel@0: end Daniel@0: elseif (option.pitch && not(isamir(x,'mirscalar'))) ... Daniel@0: || isamir(x,'mirsimatrix') Daniel@0: y = mirnovelty(x,'KernelSize',option.kernelsize); Daniel@0: type = 'mirscalar'; Daniel@0: elseif isamir(x,'mirscalar') || isamir(x,'mirenvelope') Daniel@0: y = x; %mirframenow(x,option); Daniel@0: type = mirtype(x); Daniel@0: else Daniel@0: x = mirframenow(x,option); Daniel@0: y = mirflux(x,'Inc',option.inc,'Complex',option.complex); %Not used... Daniel@0: type = 'mirscalar'; Daniel@0: end Daniel@0: Daniel@0: Daniel@0: %% MAIN Daniel@0: Daniel@0: function o = main(o,option,postoption) Daniel@0: if not(isempty(option)) && ischar(option.presel) Daniel@0: if strcmpi(option.presel,'Scheirer') Daniel@0: postoption.sampling = 200; Daniel@0: postoption.diffhwr = 1; Daniel@0: option.sum = 0; Daniel@0: postoption.detect = 0; Daniel@0: elseif strcmpi(option.presel,'Klapuri99') Daniel@0: postoption.mu = 1; Daniel@0: postoption.diffhwr = 1; Daniel@0: option.sum = 0; Daniel@0: postoption.ds = 0; Daniel@0: o2 = o; Daniel@0: end Daniel@0: end Daniel@0: if iscell(o) Daniel@0: o = o{1}; Daniel@0: end Daniel@0: if not(isempty(option)) && option.diffenv Daniel@0: postoption.diff = 1; Daniel@0: end Daniel@0: if isa(o,'mirenvelope') Daniel@0: if isfield(postoption,'sampling') && postoption.sampling Daniel@0: o = mirenvelope(o,'Sampling',postoption.sampling); Daniel@0: elseif isfield(postoption,'ds') Daniel@0: if isnan(postoption.ds) Daniel@0: if option.decim || strcmpi(option.envmeth,'Spectro') Daniel@0: postoption.ds = 0; Daniel@0: else Daniel@0: postoption.ds = 16; Daniel@0: end Daniel@0: end Daniel@0: if postoption.ds Daniel@0: o = mirenvelope(o,'Down',postoption.ds); Daniel@0: end Daniel@0: end Daniel@0: end Daniel@0: if isfield(postoption,'cthr') Daniel@0: if isa(o,'mirenvelope') Daniel@0: if postoption.mu Daniel@0: o = mirenvelope(o,'Mu'); Daniel@0: end Daniel@0: if postoption.log Daniel@0: o = mirenvelope(o,'Log'); Daniel@0: end Daniel@0: if postoption.power Daniel@0: o = mirenvelope(o,'Power'); Daniel@0: end Daniel@0: if postoption.diff Daniel@0: o = mirenvelope(o,'Diff',postoption.diff,... Daniel@0: 'Lambda',postoption.lambda,... Daniel@0: 'Complex',postoption.complex); Daniel@0: end Daniel@0: if postoption.diffhwr Daniel@0: o = mirenvelope(o,'HalfwaveDiff',postoption.diffhwr,... Daniel@0: 'Lambda',postoption.lambda,... Daniel@0: 'Complex',postoption.complex); Daniel@0: end Daniel@0: if postoption.aver Daniel@0: o = mirenvelope(o,'Smooth',postoption.aver); Daniel@0: end Daniel@0: if postoption.chwr Daniel@0: o = mirenvelope(o,'HalfwaveCenter'); Daniel@0: end Daniel@0: if postoption.c Daniel@0: o = mirenvelope(o,'Center'); Daniel@0: end Daniel@0: elseif isa(o,'mirscalar') && strcmp(get(o,'Title'),'Spectral flux') Daniel@0: if postoption.median Daniel@0: o = mirflux(o,'Median',postoption.median(1),postoption.median(2),... Daniel@0: 'Halfwave',postoption.hw); Daniel@0: else Daniel@0: o = mirflux(o,'Halfwave',postoption.hw); Daniel@0: end Daniel@0: end Daniel@0: end Daniel@0: if isfield(option,'sum') && option.sum Daniel@0: o = mirsum(o,'Adjacent',option.sum); Daniel@0: end Daniel@0: if isfield(option,'presel') && ... Daniel@0: ischar(option.presel) && strcmpi(option.presel,'Klapuri99') Daniel@0: % o, already computed, corresponds to mirenvelope(o,'Mu','HalfwaveDiff'); Daniel@0: % o is the relative distance function W in (Klapuri, 99); Daniel@0: o2 = mirenvelope(o2,'HalfwaveDiff'); Daniel@0: % o2 is the absolute distance function D in (Klapuri, 99); Daniel@0: p = mirpeaks(o,'Contrast',.2,'Chrono'); Daniel@0: p2 = mirpeaks(o2,'ScanForward',p,'Chrono'); Daniel@0: o = combinepeaks(p,p2,.05); Daniel@0: clear o2 p p2 Daniel@0: filtfreq = 44*[2.^ ([ 0:2, ( 9+(0:17) )/3 ]) ];% Center frequencies of bands Daniel@0: o = mirsum(o,'Weights',(filtfreq(1:end-1)+filtfreq(2:end))/2); Daniel@0: o = mirenvelope(o,'Smooth',12); Daniel@0: end Daniel@0: if not(isa(o,'mirscalar')) Daniel@0: o = mirframenow(o,postoption); Daniel@0: end Daniel@0: if isfield(postoption,'detect') && ischar(postoption.detect) Daniel@0: if isnan(postoption.cthr) || not(postoption.cthr) Daniel@0: if ischar(postoption.detect) || postoption.detect Daniel@0: postoption.cthr = .01; Daniel@0: end Daniel@0: elseif postoption.cthr Daniel@0: if not(ischar(postoption.detect) || postoption.detect) Daniel@0: postoption.detect = 'Peaks'; Daniel@0: end Daniel@0: end Daniel@0: if strcmpi(postoption.detect,'Peaks') Daniel@0: o = mirpeaks(o,'Total',Inf,'SelectFirst',... Daniel@0: 'Threshold',postoption.thr,'Contrast',postoption.cthr,... Daniel@0: 'Order','Abscissa','NoBegin','NoEnd'); Daniel@0: elseif strcmpi(postoption.detect,'Valleys') Daniel@0: o = mirpeaks(o,'Total',Inf,'SelectFirst',... Daniel@0: 'Threshold',postoption.thr,'Contrast',postoption.cthr,... Daniel@0: 'Valleys','Order','Abscissa','NoBegin','NoEnd'); Daniel@0: end Daniel@0: nop = cell(size(get(o,'Data'))); Daniel@0: o = set(o,'AttackPos',nop,'ReleasePos',nop); Daniel@0: end Daniel@0: if (isfield(postoption,'attack') && postoption.attack) || ... Daniel@0: (isfield(postoption,'release') && postoption.release) Daniel@0: p = get(o,'PeakPos'); Daniel@0: pm = get(o,'PeakMode'); Daniel@0: d = get(o,'Data'); Daniel@0: if postoption.attack Daniel@0: [st p pm] = mircompute(@startattack,d,p,pm); Daniel@0: end Daniel@0: if ischar(postoption.release) && ~strcmpi(postoption.release,'No') ... Daniel@0: && ~strcmpi(postoption.release,'Off') Daniel@0: [rl p pm st] = mircompute(@endrelease,d,p,pm,st,postoption.release); Daniel@0: o = set(o,'ReleasePos',rl); Daniel@0: end Daniel@0: o = set(o,'AttackPos',st,'PeakPos',p,'PeakMode',pm); Daniel@0: end Daniel@0: title = get(o,'Title'); Daniel@0: if not(length(title)>11 && strcmp(title(1:11),'Onset curve')) Daniel@0: o = set(o,'Title',['Onset curve (',title,')']); Daniel@0: end Daniel@0: Daniel@0: Daniel@0: function st = startattack(d,z,pm) Daniel@0: z = sort(z{1}); Daniel@0: pm = pm{1}; Daniel@0: st = zeros(size(z)); Daniel@0: i = 1; Daniel@0: dd = diff(d,1,1); % d' Daniel@0: ddd = diff(dd,1,1); % d'' Daniel@0: dddd = diff(ddd,1,1); % d''' Daniel@0: while i<=length(z) Daniel@0: % Start attack is identified to previous peak in d''. Daniel@0: p = find(dddd((z(i)-1)-1:-1:1)<0,1); % previous decreasing d'' Daniel@0: if isempty(p) Daniel@0: st(i) = 1; Daniel@0: else Daniel@0: n = find(dddd((z(i)-1)-p-1:-1:1)>0,1); % previous increasing d'' Daniel@0: if isempty(n) Daniel@0: st(i) = 1; Daniel@0: else Daniel@0: st(i) = ((z(i)-1)-p-(n-1))+1; Daniel@0: end Daniel@0: if i>1 && st(i-1)==st(i) Daniel@0: if d(z(i))>d(z(i-1)) Daniel@0: del = i-1; Daniel@0: else Daniel@0: del = i; Daniel@0: end Daniel@0: st(del) = []; Daniel@0: z(del) = []; Daniel@0: pm(del) = []; Daniel@0: i = i-1; Daniel@0: end Daniel@0: end Daniel@0: i = i+1; Daniel@0: end Daniel@0: st = {{st} {z} {pm}}; Daniel@0: Daniel@0: Daniel@0: function rt = endrelease(d,z,pm,st,meth) Daniel@0: z = sort(z{1}); Daniel@0: pm = pm{1}; Daniel@0: if not(isempty(st)) Daniel@0: st = st{1}; Daniel@0: end Daniel@0: rt = zeros(size(z)); Daniel@0: i = 1; Daniel@0: dd = diff(d,1,1); % d' Daniel@0: ddd = diff(dd,1,1); % d'' Daniel@0: dddd = diff(ddd,1,1); % d''' Daniel@0: while i<=length(z) Daniel@0: if strcmpi(meth,'Olivier') Daniel@0: % Release attack is identified to next (sufficiently positive) peak Daniel@0: % in d''. Daniel@0: l = find(ddd((z(i)-1):end)max(ddd)/100,1); % next increasing d'' Daniel@0: if isempty(p) Daniel@0: rt(i) = length(d); Daniel@0: else Daniel@0: n = find(dddd((z(i)-1)+(l-1)+p+1:end)<0,1); % next decreasing d'' Daniel@0: if isempty(n) Daniel@0: rt(i) = length(d); Daniel@0: else Daniel@0: rt(i) = ((z(i)-1)+(l-1)+p+n)+1; Daniel@0: end Daniel@0: end Daniel@0: end Daniel@0: elseif strcmpi(meth,'Valeri') Daniel@0: p = find(dd((z(i)-1)+1:end)>min(dd)/100,1); % find point nearest to min(dd)/100 from current peak. Daniel@0: if isempty(p) Daniel@0: rt(i) = length(d); Daniel@0: elseif p<=3 %that means if p is less than 3 points away from the peak then it can not be considered as the end point of release. Daniel@0: %Assumption is that the whole DSR(decay sustain release) section can not be shorter than 30 ms (sampling rate is 100 Hz), also, no successive note can be nearer than 30ms. Daniel@0: rt(i) = z(i)+3; Daniel@0: else Daniel@0: rt(i) = (z(i)-1)+(p-1); Daniel@0: end Daniel@0: end Daniel@0: if i>1 && rt(i-1)==rt(i) Daniel@0: if d(z(i))>d(z(i-1)) Daniel@0: del = i-1; Daniel@0: else Daniel@0: del = i; Daniel@0: end Daniel@0: rt(del) = []; Daniel@0: z(del) = []; Daniel@0: pm(del) = []; Daniel@0: if not(isempty(st)) Daniel@0: st(del) = []; Daniel@0: end Daniel@0: i = i-1; Daniel@0: end Daniel@0: i = i+1; Daniel@0: end Daniel@0: rt = {{rt} {z} {pm} {st}};