wolffd@0
|
1 classdef MTTAudioFeatureHMM < MTTAudioFeature & handle
|
wolffd@0
|
2 % ---
|
wolffd@0
|
3 % the MTTAudioFeatureBasicSm Class contains
|
wolffd@0
|
4 % a basic summary of chroma, mfcc and tempo features
|
wolffd@0
|
5 % a few common chroma and mfcc vectors are concatenated
|
wolffd@0
|
6 % along with some clip-wide variance
|
wolffd@0
|
7 % a metric / rhythm fingerprint is added
|
wolffd@0
|
8 %
|
wolffd@0
|
9 % The usual workflow for these features consists of three steps
|
wolffd@0
|
10 % 1. extract: extracts the basic single-file dependent features
|
wolffd@0
|
11 % 2. define_global_transform: calculates the global feature
|
wolffd@0
|
12 % transformation parameters
|
wolffd@0
|
13 % 3. finalise: applies the common transformations to a specific feature
|
wolffd@0
|
14 % ---
|
wolffd@0
|
15
|
wolffd@0
|
16 properties(Constant = true)
|
wolffd@0
|
17
|
wolffd@0
|
18 % svn hook
|
wolffd@0
|
19 my_revision = str2double(substr('$Rev: 2332 $', 5, -1));
|
wolffd@0
|
20 end
|
wolffd@0
|
21
|
wolffd@0
|
22 properties
|
wolffd@0
|
23 % ---
|
wolffd@0
|
24 % Set default parameters
|
wolffd@0
|
25 % ---
|
wolffd@0
|
26 my_params = struct(...
|
wolffd@0
|
27 'nstates', 4 ... % predefined number of states
|
wolffd@0
|
28 );
|
wolffd@0
|
29 end
|
wolffd@0
|
30
|
wolffd@0
|
31 % ---
|
wolffd@0
|
32 % member functions
|
wolffd@0
|
33 % ---
|
wolffd@0
|
34 methods
|
wolffd@0
|
35
|
wolffd@0
|
36 % ---
|
wolffd@0
|
37 % constructor: pointer to feature in database
|
wolffd@0
|
38 % ---
|
wolffd@0
|
39 function feature = MTTAudioFeatureHMM(varargin)
|
wolffd@0
|
40
|
wolffd@0
|
41 feature = feature@MTTAudioFeature(varargin{:});
|
wolffd@0
|
42
|
wolffd@0
|
43 end
|
wolffd@0
|
44 % ---
|
wolffd@0
|
45 % extract feature data from raw audio features
|
wolffd@0
|
46 % ---
|
wolffd@0
|
47 function data = extract(feature, clip)
|
wolffd@0
|
48 % ---
|
wolffd@0
|
49 % get Basic Summary audio features. this includes possible
|
wolffd@0
|
50 % local normalisations
|
wolffd@0
|
51 % ---
|
wolffd@0
|
52
|
wolffd@0
|
53 global globalvars;
|
wolffd@0
|
54
|
wolffd@0
|
55 % ---
|
wolffd@0
|
56 % get casimir child clip if available
|
wolffd@0
|
57 % ---
|
wolffd@0
|
58 if isa(clip, 'CASIMIRClip')
|
wolffd@0
|
59 baseclip = clip.child_clip();
|
wolffd@0
|
60 else
|
wolffd@0
|
61 baseclip = clip;
|
wolffd@0
|
62 end
|
wolffd@0
|
63 if isa(baseclip, 'MTTClip')
|
wolffd@0
|
64 rawf = baseclip.audio_features_raw();
|
wolffd@0
|
65 elseif isa(baseclip, 'MSDClip')
|
wolffd@0
|
66 rawf = baseclip.features('MSDAudioFeatureRAW');
|
wolffd@0
|
67 end
|
wolffd@0
|
68
|
wolffd@0
|
69 % ---
|
wolffd@0
|
70 % now extract the features
|
wolffd@0
|
71 % first step: chroma clustering
|
wolffd@0
|
72 % ---
|
wolffd@0
|
73 weights = [rawf.data.segments_duration];
|
wolffd@0
|
74
|
wolffd@0
|
75 % normalise weights
|
wolffd@0
|
76 weights = weights / rawf.data.duration;
|
wolffd@0
|
77
|
wolffd@0
|
78 % get the chroma features
|
wolffd@0
|
79 chroma = [rawf.data.segments_pitches]';
|
wolffd@0
|
80
|
wolffd@0
|
81 % ---
|
wolffd@0
|
82 % TODO: train hmm
|
wolffd@0
|
83 % ---
|
wolffd@0
|
84
|
wolffd@0
|
85
|
wolffd@0
|
86 % save hmm into data variable
|
wolffd@0
|
87 data.mu = mu1
|
wolffd@0
|
88 data.transmat1 = mu1
|
wolffd@0
|
89
|
wolffd@0
|
90
|
wolffd@0
|
91
|
wolffd@0
|
92
|
wolffd@0
|
93
|
wolffd@0
|
94
|
wolffd@0
|
95 % prepare field for final features
|
wolffd@0
|
96 data.final.vector = [];
|
wolffd@0
|
97 data.final.vector_info = struct();
|
wolffd@0
|
98 data.final.dim = 0;
|
wolffd@0
|
99
|
wolffd@0
|
100 % save info data
|
wolffd@0
|
101 data.info.type = 'MTTAudioFeatureBasicSm';
|
wolffd@0
|
102 data.info.owner = clip;
|
wolffd@0
|
103 data.info.owner_id = clip.id;
|
wolffd@0
|
104 data.info.creatorrev = feature.my_revision;
|
wolffd@0
|
105
|
wolffd@0
|
106 % save parameters
|
wolffd@0
|
107 data.info.params = feature.my_params;
|
wolffd@0
|
108 end
|
wolffd@0
|
109
|
wolffd@0
|
110 function define_global_transform(features)
|
wolffd@0
|
111 % calculate and set normalization factors from the group of
|
wolffd@0
|
112 % input features. These features will be set for the full database
|
wolffd@0
|
113
|
wolffd@0
|
114
|
wolffd@0
|
115
|
wolffd@0
|
116 end
|
wolffd@0
|
117
|
wolffd@0
|
118
|
wolffd@0
|
119 function finalise(feature)
|
wolffd@0
|
120 % applies a final transformation and
|
wolffd@0
|
121 % collects the information of this feature within a single vector
|
wolffd@0
|
122 % see info for types in specific dimensions
|
wolffd@0
|
123
|
wolffd@0
|
124 for i = 1:numel(feature)
|
wolffd@0
|
125
|
wolffd@0
|
126 % check for neccesary parameters
|
wolffd@0
|
127 if isempty(feature(i).my_db.commondb)
|
wolffd@0
|
128
|
wolffd@0
|
129 error('Define the global transformation first')
|
wolffd@0
|
130 return;
|
wolffd@0
|
131 end
|
wolffd@0
|
132
|
wolffd@0
|
133 if feature(1).my_params.ntimbres > 0
|
wolffd@0
|
134 % ---
|
wolffd@0
|
135 % normalise features
|
wolffd@0
|
136 % ---
|
wolffd@0
|
137 % norm timbre features if neccesary
|
wolffd@0
|
138 timbren = [];
|
wolffd@0
|
139 if feature(i).my_params.norm_timbres
|
wolffd@0
|
140 for j = 1:numel(feature(i).data.timbre)
|
wolffd@0
|
141
|
wolffd@0
|
142 timbren = cat(1, timbren, ...
|
wolffd@0
|
143 MTTAudioFeatureBasicSm.norm_timbre...
|
wolffd@0
|
144 (feature(i).data.timbre(j).means, feature(i).my_db.commondb.post_normf.timbre));
|
wolffd@0
|
145 end
|
wolffd@0
|
146 else
|
wolffd@0
|
147
|
wolffd@0
|
148 timbren = cat(1, timbren, feature(i).data.timbre(:).means);
|
wolffd@0
|
149 end
|
wolffd@0
|
150 end
|
wolffd@0
|
151
|
wolffd@0
|
152 % ---
|
wolffd@0
|
153 % construct resulting feature vector out of features
|
wolffd@0
|
154 % ---
|
wolffd@0
|
155 vec = [];
|
wolffd@0
|
156 info = {};
|
wolffd@0
|
157 if feature(i).my_params.nchromas > 0
|
wolffd@0
|
158
|
wolffd@0
|
159 info{numel(vec)+ 1} = 'chroma';
|
wolffd@0
|
160 vec = cat(1, vec, feature(i).data.chroma(:).means);
|
wolffd@0
|
161
|
wolffd@0
|
162 info{numel(vec)+ 1} = 'chroma weights';
|
wolffd@0
|
163 vec = cat(1, vec, [feature(i).data.chroma(:).means_weight]');
|
wolffd@0
|
164
|
wolffd@0
|
165 % ---
|
wolffd@0
|
166 % NORMALISE Chroma variance
|
wolffd@0
|
167 % ---
|
wolffd@0
|
168 if feature(i).my_params.chroma_var >= 1
|
wolffd@0
|
169
|
wolffd@0
|
170 info{numel(vec)+ 1} = 'chroma variance';
|
wolffd@0
|
171
|
wolffd@0
|
172 % normalise this pack of variance vectors
|
wolffd@0
|
173 tmp_var = mapminmax('apply', [feature(i).data.chroma(:).vars],...
|
wolffd@0
|
174 feature(i).common.post_normf.chroma_var);
|
wolffd@0
|
175
|
wolffd@0
|
176 % concatenate normalised data to vector
|
wolffd@0
|
177 for vari = 1:size(tmp_var,2)
|
wolffd@0
|
178
|
wolffd@0
|
179 vec = cat(1, vec, tmp_var(:, vari));
|
wolffd@0
|
180 end
|
wolffd@0
|
181 end
|
wolffd@0
|
182 end
|
wolffd@0
|
183
|
wolffd@0
|
184
|
wolffd@0
|
185 if feature(i).my_params.ntimbres > 0
|
wolffd@0
|
186
|
wolffd@0
|
187 info{numel(vec)+ 1} = 'timbre';
|
wolffd@0
|
188 vec = cat(1, vec, timbren);
|
wolffd@0
|
189
|
wolffd@0
|
190 info{numel(vec)+ 1} = 'timbre weights';
|
wolffd@0
|
191 vec = cat(1, vec, [feature(i).data.timbre(:).means_weight]');
|
wolffd@0
|
192
|
wolffd@0
|
193 % ---
|
wolffd@0
|
194 % NORMALISE timbre variance
|
wolffd@0
|
195 % ---
|
wolffd@0
|
196 if feature(i).my_params.timbre_var >= 1
|
wolffd@0
|
197
|
wolffd@0
|
198 info{numel(vec)+ 1} = 'timbre variance';
|
wolffd@0
|
199
|
wolffd@0
|
200 % normalise this pack of variance vectors
|
wolffd@0
|
201 tmp_var = mapminmax('apply', [feature(i).data.timbre(:).vars],...
|
wolffd@0
|
202 feature(i).common.post_normf.timbre_var);
|
wolffd@0
|
203
|
wolffd@0
|
204 % concatenate normalised data to vector
|
wolffd@0
|
205 for vari = 1:size(tmp_var,2)
|
wolffd@0
|
206
|
wolffd@0
|
207 vec = cat(1, vec, tmp_var(:, vari));
|
wolffd@0
|
208 end
|
wolffd@0
|
209 end
|
wolffd@0
|
210 end
|
wolffd@0
|
211
|
wolffd@0
|
212 if feature(i).my_params.nrhythms > 0
|
wolffd@0
|
213
|
wolffd@0
|
214 info{numel(vec)+ 1} = 'rhythm 8';
|
wolffd@0
|
215 vec = cat(1, vec, feature(i).data.rhythm.acorr8);
|
wolffd@0
|
216
|
wolffd@0
|
217 info{numel(vec)+ 1} = 'int 8';
|
wolffd@0
|
218 vec = cat(1, vec, feature(i).data.rhythm.interval8);
|
wolffd@0
|
219
|
wolffd@0
|
220 if feature(i).my_params.nrhythms >= 2
|
wolffd@0
|
221
|
wolffd@0
|
222 info{numel(vec)+ 1} = 'rhythm 16';
|
wolffd@0
|
223 vec = cat(1, vec, feature(i).data.rhythm.acorr16);
|
wolffd@0
|
224
|
wolffd@0
|
225 info{numel(vec)+ 1} = 'int 16';
|
wolffd@0
|
226 vec = cat(1, vec, feature(i).data.rhythm.interval16);
|
wolffd@0
|
227 end
|
wolffd@0
|
228 end
|
wolffd@0
|
229
|
wolffd@0
|
230 feature(i).data.final.vector = vec;
|
wolffd@0
|
231 feature(i).data.final.dim = numel(feature(i).data.final.vector);
|
wolffd@0
|
232
|
wolffd@0
|
233 % fill up info struct and append to feature
|
wolffd@0
|
234
|
wolffd@0
|
235 info(end+1: feature(i).data.final.dim) = ...
|
wolffd@0
|
236 cell(feature(i).data.final.dim - numel(info),1);
|
wolffd@0
|
237
|
wolffd@0
|
238 feature(i).data.final.vector_info.labels = info;
|
wolffd@0
|
239 end
|
wolffd@0
|
240
|
wolffd@0
|
241 % ---
|
wolffd@0
|
242 % TODO: Maybe delete more basic features again at this point?
|
wolffd@0
|
243 % ---
|
wolffd@0
|
244 end
|
wolffd@0
|
245
|
wolffd@0
|
246 % ---
|
wolffd@0
|
247 % destructor: do we really want to remove this
|
wolffd@0
|
248 % from the database? No, but
|
wolffd@0
|
249 % TODO: create marker for unused objects in db, and a cleanup
|
wolffd@0
|
250 % function
|
wolffd@0
|
251 % ---
|
wolffd@0
|
252 function delete(feature)
|
wolffd@0
|
253
|
wolffd@0
|
254 end
|
wolffd@0
|
255
|
wolffd@0
|
256
|
wolffd@0
|
257 function visualise(feature)
|
wolffd@0
|
258 % ---
|
wolffd@0
|
259 % plots the different data types collected in this feature
|
wolffd@0
|
260 % ---
|
wolffd@0
|
261 for i = 1:numel(feature)
|
wolffd@0
|
262 clip = feature(i).data.info.owner;
|
wolffd@0
|
263
|
wolffd@0
|
264 % display raw features
|
wolffd@0
|
265 if isa(clip, 'CASIMIRClip')
|
wolffd@0
|
266 baseclip = clip.child_clip();
|
wolffd@0
|
267 else
|
wolffd@0
|
268 baseclip = clip;
|
wolffd@0
|
269 end
|
wolffd@0
|
270 if isa(baseclip, 'MTTClip')
|
wolffd@0
|
271 rawf = baseclip.audio_features_raw();
|
wolffd@0
|
272 elseif isa(baseclip, 'MSDClip')
|
wolffd@0
|
273 rawf = baseclip.features('MSDAudioFeatureRAW');
|
wolffd@0
|
274 end
|
wolffd@0
|
275
|
wolffd@0
|
276 % ---
|
wolffd@0
|
277 % @todo: implement MSD feature visualisation
|
wolffd@0
|
278 % ---
|
wolffd@0
|
279 [a1, a2, a3] = rawf.visualise();
|
wolffd@0
|
280
|
wolffd@0
|
281 % ---
|
wolffd@0
|
282 % Display chroma features
|
wolffd@0
|
283 % ---
|
wolffd@0
|
284 if isfield(feature(i).data, 'chroma')
|
wolffd@0
|
285
|
wolffd@0
|
286 chroma_labels = {'c', 'c#', 'd','d#', 'e', 'f','f#', 'g','g#', 'a', 'a#', 'h'};
|
wolffd@0
|
287 mode_labels = {'minor', 'major'};
|
wolffd@0
|
288
|
wolffd@0
|
289 % change labels to reflect detected mode
|
wolffd@0
|
290 chroma_labels{rawf.data.key + 1} = ...
|
wolffd@0
|
291 sprintf('(%s) %s',mode_labels{rawf.data.mode + 1}, chroma_labels{rawf.data.key + 1});
|
wolffd@0
|
292
|
wolffd@0
|
293 % transpose labels and data
|
wolffd@0
|
294 chroma_labels = circshift(chroma_labels, [0, feature(i).data.chroma(1).shift]);
|
wolffd@0
|
295 chromar = circshift([rawf.data.segments_pitches], [feature(i).data.chroma(1).shift, 0]);
|
wolffd@0
|
296
|
wolffd@0
|
297 % image transposed chromas again
|
wolffd@0
|
298 segments = [rawf.data.segments_start];
|
wolffd@0
|
299 segments(end) = rawf.data.duration;
|
wolffd@0
|
300
|
wolffd@0
|
301 hold(a1);
|
wolffd@0
|
302 uimagesc(segments, 0:11, chromar, 'Parent', a1);
|
wolffd@0
|
303 set(a1,'YTick',[0:11], 'YTickLabel', chroma_labels);
|
wolffd@0
|
304
|
wolffd@0
|
305 % enlarge plot and plot new data after the old ones
|
wolffd@0
|
306 ax = axis(a1);
|
wolffd@0
|
307 ax(2) = ax(2) + 2*feature(i).my_params.nchromas + 0.5;
|
wolffd@0
|
308 axis(a1, 'xy');
|
wolffd@0
|
309 axis(a1, ax);
|
wolffd@0
|
310
|
wolffd@0
|
311 imagesc(rawf.data.duration + (1:feature(i).my_params.nchromas), (-1:11), ...
|
wolffd@0
|
312 [ feature(i).data.chroma(:).means_weight; feature(i).data.chroma(:).means],...
|
wolffd@0
|
313 'Parent', a1);
|
wolffd@0
|
314 % variance calculated?
|
wolffd@0
|
315 if isfield(feature(i).data.chroma, 'vars')
|
wolffd@0
|
316
|
wolffd@0
|
317 imagesc(rawf.data.duration + feature(i).my_params.nchromas + (1:feature(i).my_params.nchromas), (-1:11), ...
|
wolffd@0
|
318 [feature(i).data.chroma(:).vars],...
|
wolffd@0
|
319 'Parent', a1);
|
wolffd@0
|
320 end
|
wolffd@0
|
321 end
|
wolffd@0
|
322
|
wolffd@0
|
323 % ---
|
wolffd@0
|
324 % Display timbre features
|
wolffd@0
|
325 % ---
|
wolffd@0
|
326 if isfield(feature(i).data, 'timbre')
|
wolffd@0
|
327
|
wolffd@0
|
328 % enlarge plot and plot new data after the old ones
|
wolffd@0
|
329 hold(a2);
|
wolffd@0
|
330 ax = axis(a2);
|
wolffd@0
|
331 ax(2) = ax(2) + 2*feature(i).my_params.ntimbres + 0.5;
|
wolffd@0
|
332
|
wolffd@0
|
333 axis(a2, ax);
|
wolffd@0
|
334 imagesc(rawf.data.duration + (1:feature(i).my_params.ntimbres), (-1:11), ...
|
wolffd@0
|
335 [ feature(i).data.timbre(:).means_weight; feature(i).data.timbre(:).means],...
|
wolffd@0
|
336 'Parent', a2);
|
wolffd@0
|
337 if isfield(feature(i).data.timbre, 'vars')
|
wolffd@0
|
338
|
wolffd@0
|
339 imagesc(rawf.data.duration + feature(i).my_params.ntimbres + (1:feature(i).my_params.ntimbres), (-1:11), ...
|
wolffd@0
|
340 [feature(i).data.timbre(:).vars],...
|
wolffd@0
|
341 'Parent', a1);
|
wolffd@0
|
342 end
|
wolffd@0
|
343 end
|
wolffd@0
|
344
|
wolffd@0
|
345 % ---
|
wolffd@0
|
346 % Display rhythm features
|
wolffd@0
|
347 % ---
|
wolffd@0
|
348 if isfield(feature(i).data, 'rhythm')
|
wolffd@0
|
349 % data.rhythm.interval
|
wolffd@0
|
350 % get timecode
|
wolffd@0
|
351 eightt = feature(i).data.rhythm.energy8_time;
|
wolffd@0
|
352 sixt = feature(i).data.rhythm.energy16_time;
|
wolffd@0
|
353
|
wolffd@0
|
354 hold(a3);
|
wolffd@0
|
355 % plot sixteens acorr and energy
|
wolffd@0
|
356 plot(sixt, feature(i).data.rhythm.energy16, 'bx')
|
wolffd@0
|
357
|
wolffd@0
|
358 plot(sixt, feature(i).data.rhythm.acorr16, 'b')
|
wolffd@0
|
359
|
wolffd@0
|
360 % plot eights acorr and energy
|
wolffd@0
|
361 plot(eightt, feature(i).data.rhythm.energy8, 'rx')
|
wolffd@0
|
362
|
wolffd@0
|
363 plot(eightt, feature(i).data.rhythm.acorr8, 'r')
|
wolffd@0
|
364
|
wolffd@0
|
365 % broaden view by fixed 4 seconds
|
wolffd@0
|
366 ax = axis(a3);
|
wolffd@0
|
367 axis(a3, [max(0, eightt(1)-( eightt(end) - eightt(1) + 4 )) ...
|
wolffd@0
|
368 min(rawf.data.duration, eightt(end) +4) ...
|
wolffd@0
|
369 ax(3:4)]);
|
wolffd@0
|
370 end
|
wolffd@0
|
371 end
|
wolffd@0
|
372 end
|
wolffd@0
|
373 end
|
wolffd@0
|
374
|
wolffd@0
|
375
|
wolffd@0
|
376 methods (Hidden = true)
|
wolffd@0
|
377
|
wolffd@0
|
378 function [env, time] = energy_envelope(feature, clip)
|
wolffd@0
|
379 % extracts the envelope of energy for the given clip
|
wolffd@0
|
380
|
wolffd@0
|
381 % ---
|
wolffd@0
|
382 % TODO: externalise envelope etc in external audio features
|
wolffd@0
|
383 % ---
|
wolffd@0
|
384
|
wolffd@0
|
385 [null, src] = evalc('miraudio(clip.mp3file_full())');
|
wolffd@0
|
386 [null, env] = evalc('mirenvelope(src, ''Sampling'', feature.my_params.energy_sr)');
|
wolffd@0
|
387
|
wolffd@0
|
388 time = get(env,'Time');
|
wolffd@0
|
389 time = time{1}{1};
|
wolffd@0
|
390 env = mirgetdata(env);
|
wolffd@0
|
391 end
|
wolffd@0
|
392
|
wolffd@0
|
393 function [acorr, base_sig, base_t] = beat_histogram(feature, startt, interval, signal, signal_t)
|
wolffd@0
|
394 % acorr = beat_histogram(feature, startt, interval, signal, time)
|
wolffd@0
|
395 %
|
wolffd@0
|
396 % compute correlation for beats of specified length in energy curve
|
wolffd@0
|
397
|
wolffd@0
|
398 % get corresponding energy values
|
wolffd@0
|
399 dt = signal_t(2) - signal_t(1);
|
wolffd@0
|
400 base_t = startt:interval:(startt + (feature.my_params.nints*2-1) * interval);
|
wolffd@0
|
401 base_sig = signal( min( numel(signal), max(1,round((base_t - signal_t(1))/dt))));
|
wolffd@0
|
402
|
wolffd@0
|
403 % normalise energy
|
wolffd@0
|
404 acbase_sig = base_sig./max(base_sig);
|
wolffd@0
|
405
|
wolffd@0
|
406 % calculate their cyclic autocorrelation
|
wolffd@0
|
407 acorr = circshift(xcorr(acbase_sig,acbase_sig(1:end/2)),...
|
wolffd@0
|
408 [numel(acbase_sig) 0]);
|
wolffd@0
|
409
|
wolffd@0
|
410 % cut acorr to relevant points, normalise and square
|
wolffd@0
|
411 acorr = (acorr(1:feature.my_params.nints)./feature.my_params.nints).^2;
|
wolffd@0
|
412
|
wolffd@0
|
413 % ---
|
wolffd@0
|
414 % NOTE: we normalise the autocorrelation locally, to compare the
|
wolffd@0
|
415 % (rhythmic) shape
|
wolffd@0
|
416 % ---
|
wolffd@0
|
417 if feature.my_params.norm_acorr;
|
wolffd@0
|
418
|
wolffd@0
|
419 acorr = acorr - min(acorr);
|
wolffd@0
|
420 acorr = acorr/max(acorr);
|
wolffd@0
|
421 end
|
wolffd@0
|
422 end
|
wolffd@0
|
423 end
|
wolffd@0
|
424
|
wolffd@0
|
425 methods(Static)
|
wolffd@0
|
426
|
wolffd@0
|
427 function timbre = norm_timbre(in, normfs)
|
wolffd@0
|
428 % returns normed timbre data
|
wolffd@0
|
429
|
wolffd@0
|
430 % ---
|
wolffd@0
|
431 % individually scale the data using
|
wolffd@0
|
432 % the dimensions factors
|
wolffd@0
|
433 % ---
|
wolffd@0
|
434 timbre = zeros(size(in));
|
wolffd@0
|
435 for i = 1:size(in,2)
|
wolffd@0
|
436
|
wolffd@0
|
437 timbre(:,i) = normfs .* in(:,i);
|
wolffd@0
|
438 end
|
wolffd@0
|
439
|
wolffd@0
|
440 % shift to positive values
|
wolffd@0
|
441 timbre = (1 + timbre) /2;
|
wolffd@0
|
442
|
wolffd@0
|
443 % clip features to [0,1]
|
wolffd@0
|
444 timbre = min(1, max(timbre, 0));
|
wolffd@0
|
445 end
|
wolffd@0
|
446
|
wolffd@0
|
447 % ---
|
wolffd@0
|
448 % returns parameter md5 hash for comparison
|
wolffd@0
|
449 % ---
|
wolffd@0
|
450 end
|
wolffd@0
|
451
|
wolffd@0
|
452 end |