Mercurial > hg > camir-aes2014
diff toolboxes/MIRtoolbox1.3.2/MIRToolbox/@mirchromagram/mirchromagram.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/@mirchromagram/mirchromagram.m Tue Feb 10 15:05:51 2015 +0000 @@ -0,0 +1,322 @@ +function varargout = mirchromagram(orig,varargin) +% c = mirchromagram(x) computes the chromagram, or distribution of energy +% along pitches, of the audio signal x. +% (x can be the name of an audio file as well, or a spectrum, ...) +% Optional argument: +% c = mirchromagram(...,'Tuning',t): specifies the central frequency +% (in Hz.) associated to chroma C. +% Default value, t = 261.6256 Hz +% c = mirchromagram(...,'Wrap',w): specifies whether the chromagram is +% wrapped or not. +% w = 1: groups all the pitches belonging to same pitch classes +% (default value) +% w = 0: pitches are considered as absolute values. +% c = mirchromagram(...,'Frame',l,h) orders a frame decomposition of window +% length l (in seconds) and hop factor h, expressed relatively to +% the window length. For instance h = 1 indicates no overlap. +% Default values: l = .2 seconds and h = .05 +% c = mirchromagram(...,'Center'): centers the result. +% c = mirchromagram(...,'Normal',n): performs a n-norm of the +% resulting chromagram. Toggled off if n = 0 +% Default value: n = Inf (corresponding to a normalization by +% the maximum value). +% c = mirchromagram(...,'Pitch',p): specifies how to label chromas in +% the figures. +% p = 1: chromas are labeled using pitch names (default) +% alternative syntax: chromagram(...,'Pitch') +% p = 0: chromas are labeled using MIDI pitch numbers +% c = mirchromagram(...,'Triangle'): weight the contribution of each +% frequency with respect to the distance with the actual +% frequency of the corresponding chroma. +% c = mirchromagram(...,'Weight',o): specifies the relative radius of +% the weighting window, with respect to the distance between +% frequencies of successive chromas. +% o = 1: each window begins at the centers of the previous one. +% o = .5: each window begins at the end of the previous one. +% (default value) +% mirchromagram(...,'Min',mi) indicates the lowest frequency taken into +% consideration in the spectrum computation, expressed in Hz. +% Default value: 100 Hz. (Gomez, 2006) +% mirchromagram(...,'Max',ma) indicates the highest frequency taken into +% consideration in the spectrum computation, expressed in Hz. +% This upper limit is further shifted to a highest value until +% the frequency range covers an exact multiple of octaves. +% Default value: 5000 Hz. (Gomez, 2006) +% mirchromagram(...,'Res',r) indicates the resolution of the +% chromagram in number of bins per octave. +% Default value, r = 12. +% +% Gómez, E. (2006). Tonal description of music audio signal. Phd thesis, +% Universitat Pompeu Fabra, Barcelona . + + cen.key = 'Center'; + cen.type = 'Boolean'; + cen.default = 0; + option.cen = cen; + + nor.key = {'Normal','Norm'}; + nor.type = 'Integer'; + nor.default = Inf; + option.nor = nor; + + wth.key = 'Weight'; + wth.type = 'Integer'; + wth.default = .5; + option.wth = wth; + + tri.key = 'Triangle'; + tri.type = 'Boolean'; + tri.default = 0; + option.tri = tri; + + wrp.key = 'Wrap'; + wrp.type = 'Boolean'; + wrp.default = 1; + option.wrp = wrp; + + plabel.key = 'Pitch'; + plabel.type = 'Boolean'; + plabel.default = 1; + option.plabel = plabel; + + thr.key = {'Threshold','dB'}; + thr.type = 'Integer'; + thr.default = 20; + option.thr = thr; + + min.key = 'Min'; + min.type = 'Integer'; + min.default = 100; + option.min = min; + + max.key = 'Max'; + max.type = 'Integer'; + max.default = 5000; + option.max = max; + + res.key = 'Res'; + res.type = 'Integer'; + res.default = 12; + option.res = res; + + origin.key = 'Tuning'; + origin.type = 'Integer'; + origin.default = 261.6256; + option.origin = origin; + +specif.option = option; +specif.defaultframelength = .2; +specif.defaultframehop = .05; + +varargout = mirfunction(@mirchromagram,orig,varargin,nargout,specif,@init,@main); + + +function [x type] = init(x,option) +if isamir(x,'mirtemporal') || isamir(x,'mirspectrum') + freqmin = option.min; + freqmax = freqmin*2; + while freqmax < option.max + freqmax = freqmax*2; + end + %freqres = freqmin*(2.^(1/option.res)-1); + % Minimal frequency resolution should correspond to frequency range + % between the first two bins of the chromagram + + x = mirspectrum(x,'dB',option.thr,'Min',freqmin,'Max',freqmax,... + 'NormalInput','MinRes',option.res,'OctaveRatio',.85); + %freqres*.5,... + % 'WarningRes',freqres); +end +type = 'mirchromagram'; + + +function c = main(orig,option,postoption) +if iscell(orig) + orig = orig{1}; +end +if option.res == 12 + chromascale = {'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'}; +else + chromascale = 1:option.res; + option.plabel = 0; +end +if isa(orig,'mirchromagram') + c = modif(orig,option,chromascale); +else + c.plabel = 1; + c.wrap = 0; + c.chromaclass = {}; + c.chromafreq = {}; + c.register = {}; + c = class(c,'mirchromagram',mirdata(orig)); + c = purgedata(c); + c = set(c,'Title','Chromagram','Ord','magnitude','Interpolable',0); + if option.wrp + c = set(c,'Abs','chroma class'); + else + c = set(c,'Abs','chroma'); + end + m = get(orig,'Magnitude'); + f = get(orig,'Frequency'); + %disp('Computing chromagram...') + fs = get(orig,'Sampling'); + n = cell(1,length(m)); % The final structured list of magnitudes. + cc = cell(1,length(m)); % The final structured list of chroma classes. + o = cell(1,length(m)); % The final structured list of octave registers. + p = cell(1,length(m)); % The final structured list of chromas. + cf = cell(1,length(m)); % The final structured list of central frequencies related to chromas. + for i = 1:length(m) + mi = m{i}; + fi = f{i}; + if not(iscell(mi)) + mi = {mi}; + fi = {fi}; + end + ni = cell(1,length(mi)); % The list of magnitudes. + ci = cell(1,length(mi)); % The list of chroma classes. + oi = cell(1,length(mi)); % The list of octave registers. + pi = cell(1,length(mi)); % The list of absolute chromas. + cfi = cell(1,length(mi)); % The central frequency of each chroma. + for j = 1:length(mi) + mj = mi{j}; + fj = fi{j}; + + % Let's remove the frequencies exceeding the last whole octave. + minfj = min(min(min(fj))); + maxfj = max(max(max(fj))); + maxfj = minfj*2^(floor(log2(maxfj/minfj))); + fz = find(fj(:,1,1,1) > maxfj); + mj(fz,:,:,:) = []; + fj(fz,:,:,:) = []; + + [s1 s2 s3] = size(mj); + + cj = freq2chro(fj,option.res,option.origin); + if not(ismember(min(cj)+1,cj)) + warning('WARNING IN MIRCHROMAGRAM: Frequency resolution of the spectrum is too low.'); + display('The conversion of low frequencies into chromas may be incorrect.'); + end + ccj = min(min(min(cj))):max(max(max(cj))); + sc = length(ccj); % The size of range of absolute chromas. + mat = zeros(s1,sc); + fc = chro2freq(ccj,option.res,option.origin); % The absolute chromas in Hz. + fl = chro2freq(ccj-1,option.res,option.origin); % Each previous chromas in Hz. + fr = chro2freq(ccj+1,option.res,option.origin); % Each related next chromas in Hz. + for k = 1:sc + rad = find(and(fj(:,1) > fc(k)-option.wth*(fc(k)-fl(k)),... + fj(:,1) < fc(k)-option.wth*(fc(k)-fr(k)))); + if option.tri + dist = fc(k) - fj(:,1,1,1); + rad1 = dist/(fc(k) - fl(k))/option.wth; + rad2 = dist/(fc(k) - fr(k))/option.wth; + ndist = max(rad1,rad2); + mat(:,k) = max(min(1-ndist,1),0)/length(rad); + else + mat(rad,k) = ones(length(rad),1)/length(rad); + end + if k ==1 || k == sc + mat(:,k) = mat(:,k)/2; + end + end + nj = zeros(sc,s2,s3); + for k = 1:s2 + for l = 1:s3 + nj(:,k,l) = (mj(:,k,l)'*mat)'; + end + end + cj = mod(ccj',option.res); + oi{j} = floor(ccj/option.res)+4; + if option.plabel + pj = strcat(chromascale(cj+1)',num2str(oi{j}')); + else + pj = ccj'+60; + end + ci{j} = repmat(cj,[1,s2,s3]); + pi{j} = repmat(pj,[1,s2,s3]); + ni{j} = nj; + cfi{j} = fc; + end + n{i} = ni; + cc{i} = ci; + o{i} = oi; + p{i} = pi; + cf{i} = cfi; + end + c = set(c,'Magnitude',n,'Chroma',p,'ChromaClass',cc,... + 'ChromaFreq',cf,'Register',o); + c = modif(c,option,chromascale); + c = {c orig}; +end + + +function c = modif(c,option,chromascale) +if option.plabel + c = set(c,'PitchLabel',1); +end +if option.cen || option.nor || option.wrp + n = get(c,'Magnitude'); + p = get(c,'Chroma'); + cl = get(c,'ChromaClass'); + fp = get(c,'FramePos'); + n2 = cell(1,length(n)); + p2 = cell(1,length(n)); + wrp = option.wrp && not(get(c,'Wrap')); + for i = 1:length(n) + ni = n{i}; + pi = p{i}; + cli = cl{i}; + if not(iscell(ni)) + ni = {ni}; + pi = {pi}; + cli = {cli}; + end + if wrp + c = set(c,'Wrap',option.wrp); + end + n2i = cell(1,length(ni)); + p2i = cell(1,length(ni)); + for j = 1:length(ni) + nj = ni{j}; + pj = pi{j}; + clj = cli{j}; + if wrp + n2j = zeros(option.res,size(nj,2),size(nj,3)); + for k = 1:size(pj,1) + n2j(clj(k)+1,:,:) = n2j(clj(k)+1,:,:) + nj(k,:,:); % squared sum (parameter) + end + p2i{j} = chromascale'; + else + n2j = nj; + p2i{j} = pi{j}; + end + if option.cen + n2j = n2j - repmat(mean(n2j),[size(n2j,1),1,1]); + end + if option.nor + n2j = n2j ./ repmat(vectnorm(n2j,option.nor) + ... + repmat(1e-6,[1,size(n2j,2),size(n2j,3)] )... + ,[size(n2j,1),1,1]); + end + n2i{j} = n2j; + end + n2{i} = n2i; + p2{i} = p2i; + end + c = set(c,'Magnitude',n2,'Chroma',p2,'FramePos',fp); +end + + +function c = freq2chro(f,res,origin) +c = round(res*log2(f/origin)); + + +function f = chro2freq(c,res,origin) +f = 2.^(c/res)*origin; + + +function y = vectnorm(x,p) +if isinf(p) + y = max(x); +else + y = sum(abs(x).^p).^(1/p); +end \ No newline at end of file