wolffd@0
|
1 function varargout = mirchromagram(orig,varargin)
|
wolffd@0
|
2 % c = mirchromagram(x) computes the chromagram, or distribution of energy
|
wolffd@0
|
3 % along pitches, of the audio signal x.
|
wolffd@0
|
4 % (x can be the name of an audio file as well, or a spectrum, ...)
|
wolffd@0
|
5 % Optional argument:
|
wolffd@0
|
6 % c = mirchromagram(...,'Tuning',t): specifies the central frequency
|
wolffd@0
|
7 % (in Hz.) associated to chroma C.
|
wolffd@0
|
8 % Default value, t = 261.6256 Hz
|
wolffd@0
|
9 % c = mirchromagram(...,'Wrap',w): specifies whether the chromagram is
|
wolffd@0
|
10 % wrapped or not.
|
wolffd@0
|
11 % w = 1: groups all the pitches belonging to same pitch classes
|
wolffd@0
|
12 % (default value)
|
wolffd@0
|
13 % w = 0: pitches are considered as absolute values.
|
wolffd@0
|
14 % c = mirchromagram(...,'Frame',l,h) orders a frame decomposition of window
|
wolffd@0
|
15 % length l (in seconds) and hop factor h, expressed relatively to
|
wolffd@0
|
16 % the window length. For instance h = 1 indicates no overlap.
|
wolffd@0
|
17 % Default values: l = .2 seconds and h = .05
|
wolffd@0
|
18 % c = mirchromagram(...,'Center'): centers the result.
|
wolffd@0
|
19 % c = mirchromagram(...,'Normal',n): performs a n-norm of the
|
wolffd@0
|
20 % resulting chromagram. Toggled off if n = 0
|
wolffd@0
|
21 % Default value: n = Inf (corresponding to a normalization by
|
wolffd@0
|
22 % the maximum value).
|
wolffd@0
|
23 % c = mirchromagram(...,'Pitch',p): specifies how to label chromas in
|
wolffd@0
|
24 % the figures.
|
wolffd@0
|
25 % p = 1: chromas are labeled using pitch names (default)
|
wolffd@0
|
26 % alternative syntax: chromagram(...,'Pitch')
|
wolffd@0
|
27 % p = 0: chromas are labeled using MIDI pitch numbers
|
wolffd@0
|
28 % c = mirchromagram(...,'Triangle'): weight the contribution of each
|
wolffd@0
|
29 % frequency with respect to the distance with the actual
|
wolffd@0
|
30 % frequency of the corresponding chroma.
|
wolffd@0
|
31 % c = mirchromagram(...,'Weight',o): specifies the relative radius of
|
wolffd@0
|
32 % the weighting window, with respect to the distance between
|
wolffd@0
|
33 % frequencies of successive chromas.
|
wolffd@0
|
34 % o = 1: each window begins at the centers of the previous one.
|
wolffd@0
|
35 % o = .5: each window begins at the end of the previous one.
|
wolffd@0
|
36 % (default value)
|
wolffd@0
|
37 % mirchromagram(...,'Min',mi) indicates the lowest frequency taken into
|
wolffd@0
|
38 % consideration in the spectrum computation, expressed in Hz.
|
wolffd@0
|
39 % Default value: 100 Hz. (Gomez, 2006)
|
wolffd@0
|
40 % mirchromagram(...,'Max',ma) indicates the highest frequency taken into
|
wolffd@0
|
41 % consideration in the spectrum computation, expressed in Hz.
|
wolffd@0
|
42 % This upper limit is further shifted to a highest value until
|
wolffd@0
|
43 % the frequency range covers an exact multiple of octaves.
|
wolffd@0
|
44 % Default value: 5000 Hz. (Gomez, 2006)
|
wolffd@0
|
45 % mirchromagram(...,'Res',r) indicates the resolution of the
|
wolffd@0
|
46 % chromagram in number of bins per octave.
|
wolffd@0
|
47 % Default value, r = 12.
|
wolffd@0
|
48 %
|
wolffd@0
|
49 % Gómez, E. (2006). Tonal description of music audio signal. Phd thesis,
|
wolffd@0
|
50 % Universitat Pompeu Fabra, Barcelona .
|
wolffd@0
|
51
|
wolffd@0
|
52 cen.key = 'Center';
|
wolffd@0
|
53 cen.type = 'Boolean';
|
wolffd@0
|
54 cen.default = 0;
|
wolffd@0
|
55 option.cen = cen;
|
wolffd@0
|
56
|
wolffd@0
|
57 nor.key = {'Normal','Norm'};
|
wolffd@0
|
58 nor.type = 'Integer';
|
wolffd@0
|
59 nor.default = Inf;
|
wolffd@0
|
60 option.nor = nor;
|
wolffd@0
|
61
|
wolffd@0
|
62 wth.key = 'Weight';
|
wolffd@0
|
63 wth.type = 'Integer';
|
wolffd@0
|
64 wth.default = .5;
|
wolffd@0
|
65 option.wth = wth;
|
wolffd@0
|
66
|
wolffd@0
|
67 tri.key = 'Triangle';
|
wolffd@0
|
68 tri.type = 'Boolean';
|
wolffd@0
|
69 tri.default = 0;
|
wolffd@0
|
70 option.tri = tri;
|
wolffd@0
|
71
|
wolffd@0
|
72 wrp.key = 'Wrap';
|
wolffd@0
|
73 wrp.type = 'Boolean';
|
wolffd@0
|
74 wrp.default = 1;
|
wolffd@0
|
75 option.wrp = wrp;
|
wolffd@0
|
76
|
wolffd@0
|
77 plabel.key = 'Pitch';
|
wolffd@0
|
78 plabel.type = 'Boolean';
|
wolffd@0
|
79 plabel.default = 1;
|
wolffd@0
|
80 option.plabel = plabel;
|
wolffd@0
|
81
|
wolffd@0
|
82 thr.key = {'Threshold','dB'};
|
wolffd@0
|
83 thr.type = 'Integer';
|
wolffd@0
|
84 thr.default = 20;
|
wolffd@0
|
85 option.thr = thr;
|
wolffd@0
|
86
|
wolffd@0
|
87 min.key = 'Min';
|
wolffd@0
|
88 min.type = 'Integer';
|
wolffd@0
|
89 min.default = 100;
|
wolffd@0
|
90 option.min = min;
|
wolffd@0
|
91
|
wolffd@0
|
92 max.key = 'Max';
|
wolffd@0
|
93 max.type = 'Integer';
|
wolffd@0
|
94 max.default = 5000;
|
wolffd@0
|
95 option.max = max;
|
wolffd@0
|
96
|
wolffd@0
|
97 res.key = 'Res';
|
wolffd@0
|
98 res.type = 'Integer';
|
wolffd@0
|
99 res.default = 12;
|
wolffd@0
|
100 option.res = res;
|
wolffd@0
|
101
|
wolffd@0
|
102 origin.key = 'Tuning';
|
wolffd@0
|
103 origin.type = 'Integer';
|
wolffd@0
|
104 origin.default = 261.6256;
|
wolffd@0
|
105 option.origin = origin;
|
wolffd@0
|
106
|
wolffd@0
|
107 specif.option = option;
|
wolffd@0
|
108 specif.defaultframelength = .2;
|
wolffd@0
|
109 specif.defaultframehop = .05;
|
wolffd@0
|
110
|
wolffd@0
|
111 varargout = mirfunction(@mirchromagram,orig,varargin,nargout,specif,@init,@main);
|
wolffd@0
|
112
|
wolffd@0
|
113
|
wolffd@0
|
114 function [x type] = init(x,option)
|
wolffd@0
|
115 if isamir(x,'mirtemporal') || isamir(x,'mirspectrum')
|
wolffd@0
|
116 freqmin = option.min;
|
wolffd@0
|
117 freqmax = freqmin*2;
|
wolffd@0
|
118 while freqmax < option.max
|
wolffd@0
|
119 freqmax = freqmax*2;
|
wolffd@0
|
120 end
|
wolffd@0
|
121 %freqres = freqmin*(2.^(1/option.res)-1);
|
wolffd@0
|
122 % Minimal frequency resolution should correspond to frequency range
|
wolffd@0
|
123 % between the first two bins of the chromagram
|
wolffd@0
|
124
|
wolffd@0
|
125 x = mirspectrum(x,'dB',option.thr,'Min',freqmin,'Max',freqmax,...
|
wolffd@0
|
126 'NormalInput','MinRes',option.res,'OctaveRatio',.85);
|
wolffd@0
|
127 %freqres*.5,...
|
wolffd@0
|
128 % 'WarningRes',freqres);
|
wolffd@0
|
129 end
|
wolffd@0
|
130 type = 'mirchromagram';
|
wolffd@0
|
131
|
wolffd@0
|
132
|
wolffd@0
|
133 function c = main(orig,option,postoption)
|
wolffd@0
|
134 if iscell(orig)
|
wolffd@0
|
135 orig = orig{1};
|
wolffd@0
|
136 end
|
wolffd@0
|
137 if option.res == 12
|
wolffd@0
|
138 chromascale = {'C','C#','D','D#','E','F','F#','G','G#','A','A#','B'};
|
wolffd@0
|
139 else
|
wolffd@0
|
140 chromascale = 1:option.res;
|
wolffd@0
|
141 option.plabel = 0;
|
wolffd@0
|
142 end
|
wolffd@0
|
143 if isa(orig,'mirchromagram')
|
wolffd@0
|
144 c = modif(orig,option,chromascale);
|
wolffd@0
|
145 else
|
wolffd@0
|
146 c.plabel = 1;
|
wolffd@0
|
147 c.wrap = 0;
|
wolffd@0
|
148 c.chromaclass = {};
|
wolffd@0
|
149 c.chromafreq = {};
|
wolffd@0
|
150 c.register = {};
|
wolffd@0
|
151 c = class(c,'mirchromagram',mirdata(orig));
|
wolffd@0
|
152 c = purgedata(c);
|
wolffd@0
|
153 c = set(c,'Title','Chromagram','Ord','magnitude','Interpolable',0);
|
wolffd@0
|
154 if option.wrp
|
wolffd@0
|
155 c = set(c,'Abs','chroma class');
|
wolffd@0
|
156 else
|
wolffd@0
|
157 c = set(c,'Abs','chroma');
|
wolffd@0
|
158 end
|
wolffd@0
|
159 m = get(orig,'Magnitude');
|
wolffd@0
|
160 f = get(orig,'Frequency');
|
wolffd@0
|
161 %disp('Computing chromagram...')
|
wolffd@0
|
162 fs = get(orig,'Sampling');
|
wolffd@0
|
163 n = cell(1,length(m)); % The final structured list of magnitudes.
|
wolffd@0
|
164 cc = cell(1,length(m)); % The final structured list of chroma classes.
|
wolffd@0
|
165 o = cell(1,length(m)); % The final structured list of octave registers.
|
wolffd@0
|
166 p = cell(1,length(m)); % The final structured list of chromas.
|
wolffd@0
|
167 cf = cell(1,length(m)); % The final structured list of central frequencies related to chromas.
|
wolffd@0
|
168 for i = 1:length(m)
|
wolffd@0
|
169 mi = m{i};
|
wolffd@0
|
170 fi = f{i};
|
wolffd@0
|
171 if not(iscell(mi))
|
wolffd@0
|
172 mi = {mi};
|
wolffd@0
|
173 fi = {fi};
|
wolffd@0
|
174 end
|
wolffd@0
|
175 ni = cell(1,length(mi)); % The list of magnitudes.
|
wolffd@0
|
176 ci = cell(1,length(mi)); % The list of chroma classes.
|
wolffd@0
|
177 oi = cell(1,length(mi)); % The list of octave registers.
|
wolffd@0
|
178 pi = cell(1,length(mi)); % The list of absolute chromas.
|
wolffd@0
|
179 cfi = cell(1,length(mi)); % The central frequency of each chroma.
|
wolffd@0
|
180 for j = 1:length(mi)
|
wolffd@0
|
181 mj = mi{j};
|
wolffd@0
|
182 fj = fi{j};
|
wolffd@0
|
183
|
wolffd@0
|
184 % Let's remove the frequencies exceeding the last whole octave.
|
wolffd@0
|
185 minfj = min(min(min(fj)));
|
wolffd@0
|
186 maxfj = max(max(max(fj)));
|
wolffd@0
|
187 maxfj = minfj*2^(floor(log2(maxfj/minfj)));
|
wolffd@0
|
188 fz = find(fj(:,1,1,1) > maxfj);
|
wolffd@0
|
189 mj(fz,:,:,:) = [];
|
wolffd@0
|
190 fj(fz,:,:,:) = [];
|
wolffd@0
|
191
|
wolffd@0
|
192 [s1 s2 s3] = size(mj);
|
wolffd@0
|
193
|
wolffd@0
|
194 cj = freq2chro(fj,option.res,option.origin);
|
wolffd@0
|
195 if not(ismember(min(cj)+1,cj))
|
wolffd@0
|
196 warning('WARNING IN MIRCHROMAGRAM: Frequency resolution of the spectrum is too low.');
|
wolffd@0
|
197 display('The conversion of low frequencies into chromas may be incorrect.');
|
wolffd@0
|
198 end
|
wolffd@0
|
199 ccj = min(min(min(cj))):max(max(max(cj)));
|
wolffd@0
|
200 sc = length(ccj); % The size of range of absolute chromas.
|
wolffd@0
|
201 mat = zeros(s1,sc);
|
wolffd@0
|
202 fc = chro2freq(ccj,option.res,option.origin); % The absolute chromas in Hz.
|
wolffd@0
|
203 fl = chro2freq(ccj-1,option.res,option.origin); % Each previous chromas in Hz.
|
wolffd@0
|
204 fr = chro2freq(ccj+1,option.res,option.origin); % Each related next chromas in Hz.
|
wolffd@0
|
205 for k = 1:sc
|
wolffd@0
|
206 rad = find(and(fj(:,1) > fc(k)-option.wth*(fc(k)-fl(k)),...
|
wolffd@0
|
207 fj(:,1) < fc(k)-option.wth*(fc(k)-fr(k))));
|
wolffd@0
|
208 if option.tri
|
wolffd@0
|
209 dist = fc(k) - fj(:,1,1,1);
|
wolffd@0
|
210 rad1 = dist/(fc(k) - fl(k))/option.wth;
|
wolffd@0
|
211 rad2 = dist/(fc(k) - fr(k))/option.wth;
|
wolffd@0
|
212 ndist = max(rad1,rad2);
|
wolffd@0
|
213 mat(:,k) = max(min(1-ndist,1),0)/length(rad);
|
wolffd@0
|
214 else
|
wolffd@0
|
215 mat(rad,k) = ones(length(rad),1)/length(rad);
|
wolffd@0
|
216 end
|
wolffd@0
|
217 if k ==1 || k == sc
|
wolffd@0
|
218 mat(:,k) = mat(:,k)/2;
|
wolffd@0
|
219 end
|
wolffd@0
|
220 end
|
wolffd@0
|
221 nj = zeros(sc,s2,s3);
|
wolffd@0
|
222 for k = 1:s2
|
wolffd@0
|
223 for l = 1:s3
|
wolffd@0
|
224 nj(:,k,l) = (mj(:,k,l)'*mat)';
|
wolffd@0
|
225 end
|
wolffd@0
|
226 end
|
wolffd@0
|
227 cj = mod(ccj',option.res);
|
wolffd@0
|
228 oi{j} = floor(ccj/option.res)+4;
|
wolffd@0
|
229 if option.plabel
|
wolffd@0
|
230 pj = strcat(chromascale(cj+1)',num2str(oi{j}'));
|
wolffd@0
|
231 else
|
wolffd@0
|
232 pj = ccj'+60;
|
wolffd@0
|
233 end
|
wolffd@0
|
234 ci{j} = repmat(cj,[1,s2,s3]);
|
wolffd@0
|
235 pi{j} = repmat(pj,[1,s2,s3]);
|
wolffd@0
|
236 ni{j} = nj;
|
wolffd@0
|
237 cfi{j} = fc;
|
wolffd@0
|
238 end
|
wolffd@0
|
239 n{i} = ni;
|
wolffd@0
|
240 cc{i} = ci;
|
wolffd@0
|
241 o{i} = oi;
|
wolffd@0
|
242 p{i} = pi;
|
wolffd@0
|
243 cf{i} = cfi;
|
wolffd@0
|
244 end
|
wolffd@0
|
245 c = set(c,'Magnitude',n,'Chroma',p,'ChromaClass',cc,...
|
wolffd@0
|
246 'ChromaFreq',cf,'Register',o);
|
wolffd@0
|
247 c = modif(c,option,chromascale);
|
wolffd@0
|
248 c = {c orig};
|
wolffd@0
|
249 end
|
wolffd@0
|
250
|
wolffd@0
|
251
|
wolffd@0
|
252 function c = modif(c,option,chromascale)
|
wolffd@0
|
253 if option.plabel
|
wolffd@0
|
254 c = set(c,'PitchLabel',1);
|
wolffd@0
|
255 end
|
wolffd@0
|
256 if option.cen || option.nor || option.wrp
|
wolffd@0
|
257 n = get(c,'Magnitude');
|
wolffd@0
|
258 p = get(c,'Chroma');
|
wolffd@0
|
259 cl = get(c,'ChromaClass');
|
wolffd@0
|
260 fp = get(c,'FramePos');
|
wolffd@0
|
261 n2 = cell(1,length(n));
|
wolffd@0
|
262 p2 = cell(1,length(n));
|
wolffd@0
|
263 wrp = option.wrp && not(get(c,'Wrap'));
|
wolffd@0
|
264 for i = 1:length(n)
|
wolffd@0
|
265 ni = n{i};
|
wolffd@0
|
266 pi = p{i};
|
wolffd@0
|
267 cli = cl{i};
|
wolffd@0
|
268 if not(iscell(ni))
|
wolffd@0
|
269 ni = {ni};
|
wolffd@0
|
270 pi = {pi};
|
wolffd@0
|
271 cli = {cli};
|
wolffd@0
|
272 end
|
wolffd@0
|
273 if wrp
|
wolffd@0
|
274 c = set(c,'Wrap',option.wrp);
|
wolffd@0
|
275 end
|
wolffd@0
|
276 n2i = cell(1,length(ni));
|
wolffd@0
|
277 p2i = cell(1,length(ni));
|
wolffd@0
|
278 for j = 1:length(ni)
|
wolffd@0
|
279 nj = ni{j};
|
wolffd@0
|
280 pj = pi{j};
|
wolffd@0
|
281 clj = cli{j};
|
wolffd@0
|
282 if wrp
|
wolffd@0
|
283 n2j = zeros(option.res,size(nj,2),size(nj,3));
|
wolffd@0
|
284 for k = 1:size(pj,1)
|
wolffd@0
|
285 n2j(clj(k)+1,:,:) = n2j(clj(k)+1,:,:) + nj(k,:,:); % squared sum (parameter)
|
wolffd@0
|
286 end
|
wolffd@0
|
287 p2i{j} = chromascale';
|
wolffd@0
|
288 else
|
wolffd@0
|
289 n2j = nj;
|
wolffd@0
|
290 p2i{j} = pi{j};
|
wolffd@0
|
291 end
|
wolffd@0
|
292 if option.cen
|
wolffd@0
|
293 n2j = n2j - repmat(mean(n2j),[size(n2j,1),1,1]);
|
wolffd@0
|
294 end
|
wolffd@0
|
295 if option.nor
|
wolffd@0
|
296 n2j = n2j ./ repmat(vectnorm(n2j,option.nor) + ...
|
wolffd@0
|
297 repmat(1e-6,[1,size(n2j,2),size(n2j,3)] )...
|
wolffd@0
|
298 ,[size(n2j,1),1,1]);
|
wolffd@0
|
299 end
|
wolffd@0
|
300 n2i{j} = n2j;
|
wolffd@0
|
301 end
|
wolffd@0
|
302 n2{i} = n2i;
|
wolffd@0
|
303 p2{i} = p2i;
|
wolffd@0
|
304 end
|
wolffd@0
|
305 c = set(c,'Magnitude',n2,'Chroma',p2,'FramePos',fp);
|
wolffd@0
|
306 end
|
wolffd@0
|
307
|
wolffd@0
|
308
|
wolffd@0
|
309 function c = freq2chro(f,res,origin)
|
wolffd@0
|
310 c = round(res*log2(f/origin));
|
wolffd@0
|
311
|
wolffd@0
|
312
|
wolffd@0
|
313 function f = chro2freq(c,res,origin)
|
wolffd@0
|
314 f = 2.^(c/res)*origin;
|
wolffd@0
|
315
|
wolffd@0
|
316
|
wolffd@0
|
317 function y = vectnorm(x,p)
|
wolffd@0
|
318 if isinf(p)
|
wolffd@0
|
319 y = max(x);
|
wolffd@0
|
320 else
|
wolffd@0
|
321 y = sum(abs(x).^p).^(1/p);
|
wolffd@0
|
322 end |