holger@0
|
1 classdef CAdaptInstrSpec
|
holger@0
|
2 % CAdaptInstrSpec - Estimation of a filter curve that enables the
|
holger@0
|
3 % adaptation of instrument templates from one recording to another. The
|
holger@0
|
4 % beta-divergence is used as a cost function between the original and the
|
holger@0
|
5 % adapted spectra. All spectra need to be provided on a logarithmic
|
holger@0
|
6 % frequency axis (for now, an extension to linear frequency axes should
|
holger@0
|
7 % be straightforward). For further details on the estimation method, see
|
holger@0
|
8 % the references below.
|
holger@0
|
9 %
|
holger@0
|
10 % PROPERTIES
|
holger@0
|
11 % no public properties
|
holger@0
|
12 %
|
holger@0
|
13 % METHODS
|
holger@0
|
14 % CAdaptInstrSpec - constructor for CAdaptInstrSpec object
|
holger@0
|
15 % setH - sets filter curve 'h'
|
holger@0
|
16 % getH - returns estimate of filter curve ''h''
|
holger@0
|
17 % getSmoothedH - returns smoothed and interpolated version of the
|
holger@0
|
18 % filter curve ''h''
|
holger@0
|
19 % updateH - performs single update of filter curve ''h''
|
holger@0
|
20 % estimateSpectra - estimates spectra based on current estimate of
|
holger@0
|
21 % the filter curve.
|
holger@0
|
22 % compBetaDivergence - compute beta-divergence between original and
|
holger@0
|
23 % estimated spectra
|
holger@0
|
24 %
|
holger@0
|
25 % For further help on the methods, type 'help CAdaptInstrSpec.[methodName]'
|
holger@0
|
26 %
|
holger@0
|
27 %
|
holger@0
|
28 % References:
|
holger@0
|
29 %
|
holger@0
|
30 % [1] H. Kirchhoff, S. Dixon, A. Klapuri. Missing spectral templates
|
holger@0
|
31 % estimation for user-assisted music transcription. IEEE International
|
holger@0
|
32 % Conference on Acoustics, Speech and Signal Processing, Vancouver,
|
holger@0
|
33 % Canada, 2013, submitted.
|
holger@0
|
34 % [2] H. Kirchhoff, S. Dixon, and A. Klapuri. Cross-recording adaptation of
|
holger@0
|
35 % musical instrument spectra. Technical Report C4DM-TR-11-2012,
|
holger@0
|
36 % Queen Mary University of London, 2012.
|
holger@0
|
37 % http://www.eecs.qmul.ac.uk/~holger/C4DM-TR-11-2012
|
holger@0
|
38
|
holger@0
|
39 % Copyright (C) 2012 Holger Kirchhoff
|
holger@0
|
40 %
|
holger@0
|
41 % This program is free software; you can redistribute it and/or
|
holger@0
|
42 % modify it under the terms of the GNU General Public License
|
holger@0
|
43 % as published by the Free Software Foundation; either version 2
|
holger@0
|
44 % of the License, or (at your option) any later version.
|
holger@0
|
45 %
|
holger@0
|
46 % This program is distributed in the hope that it will be useful,
|
holger@0
|
47 % but WITHOUT ANY WARRANTY; without even the implied warranty of
|
holger@0
|
48 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
holger@0
|
49 % GNU General Public License for more details.
|
holger@0
|
50 %
|
holger@0
|
51 % You should have received a copy of the GNU General Public License
|
holger@0
|
52 % along with this program; if not, write to the Free Software
|
holger@0
|
53 % Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
holger@0
|
54
|
holger@0
|
55
|
holger@0
|
56 properties (Access='private')
|
holger@0
|
57
|
holger@0
|
58 h = []; % filter transfer function
|
holger@0
|
59 maxDevFromMedianInDB = 10; % maximum deviation from mean values
|
holger@0
|
60 numCepstralCoeffs = 20;
|
holger@0
|
61 regularisationParam = 0.001;
|
holger@0
|
62
|
holger@0
|
63 spectra_DB = []; % basis functions estimated from database (e.g. RWC)
|
holger@0
|
64 spectra_data = []; % basis functions derived from analysis spectrogram
|
holger@0
|
65 W_DB = []; % spectra_DB reduced to peak amplitudes only
|
holger@0
|
66 W_data = []; % spectra_data reduced to peak amplitudes only
|
holger@0
|
67 W_data_est = []; % estimated basis functions (W_data * h)
|
holger@0
|
68 f0Idcs_DB = []; % pitch values of columns in W_DB
|
holger@0
|
69 f0Idcs_data = []; % pitch values of columns in W_data
|
holger@0
|
70 numF0Idcs_DB = 0;
|
holger@0
|
71 numF0Idcs_data = 0;
|
holger@0
|
72
|
holger@0
|
73 commonF0Idcs = []; % midi pitch values that occur both in f0Idcs_DB and in f0Idcs_data
|
holger@0
|
74 commonF0IdcsIdcs_DB = []; % pitch indices in f0Idcs_DB that also occur in f0Idcs_data
|
holger@0
|
75 commonF0IdcsIdcs_data = []; % pitch indices in f0Idcs_data that also occur in f0Idcs_DB
|
holger@0
|
76 numCommonShifts = 0;
|
holger@0
|
77
|
holger@0
|
78 zeroIdcsW_data = []; % indices in W_data that are zero (required for numerical reasons)
|
holger@0
|
79 zeroIdcsW_data_est = []; % indices in W_DB that are zero
|
holger@0
|
80 zeroIdcsH = []; % indices in h that are zero
|
holger@0
|
81
|
holger@0
|
82 maxNumFreqs = 0;
|
holger@0
|
83
|
holger@0
|
84 costFctName = '';
|
holger@0
|
85 costFctNames = {'LS', 'KL', 'IS', 'BD'};
|
holger@0
|
86
|
holger@0
|
87 beta = 0; % parameter beta for beta divergence
|
holger@0
|
88
|
holger@0
|
89 end % properties
|
holger@0
|
90
|
holger@0
|
91 methods
|
holger@0
|
92
|
holger@0
|
93 function obj = CAdaptInstrSpec(spectra_DB, spectra_data, f0Idcs_DB, f0Idcs_data, numBinsPerSemitone, costFctName, varargin)
|
holger@0
|
94 % CAdaptInstrSpec - constructor of CAdaptInstrSpec class
|
holger@0
|
95 %
|
holger@0
|
96 % myObj = CAdaptInstrSpec(spectra_DB, spectra_data, f0Idcs_DB,
|
holger@0
|
97 % f0Idcs_data, numBinsPerSemitone, costFctName)
|
holger@0
|
98 % constructs a CAdaptInstrSpec object.
|
holger@0
|
99 %
|
holger@0
|
100 % Parameters:
|
holger@0
|
101 % spectra_DB - matrix containing in its columns the the
|
holger@0
|
102 % database templates that are to be adapted
|
holger@0
|
103 % spectra_data - matrix containing the spectra estimated
|
holger@0
|
104 % from the recording to which the database
|
holger@0
|
105 % spectra should be adapted
|
holger@0
|
106 % f0Idcs_DB - f0-indices corresponding to the columns
|
holger@0
|
107 % in spectra_DB
|
holger@0
|
108 % f0Idcs_data - f0-indices corresponding to the columns
|
holger@0
|
109 % in spectra_data
|
holger@0
|
110 % numBinsPerSemitone - pitch resolution of the constant-Q
|
holger@0
|
111 % spectra
|
holger@0
|
112 % costFctName - name of cost function. available cost
|
holger@0
|
113 % functions are:
|
holger@0
|
114 % 'LS' - least squares error
|
holger@0
|
115 % 'KL' - generalised Kullback-Leibler div.
|
holger@0
|
116 % 'IS' - Itakura-Saito divergence
|
holger@0
|
117 % 'BD' - beta divergence
|
holger@0
|
118 %
|
holger@0
|
119 % If 'BD' is selected as the cost function, the parameter beta
|
holger@0
|
120 % has to be provided by myObj = CAdaptInstrSpec(..., 'beta',
|
holger@0
|
121 % betaValue) where betaValue is a real, finite scalar.
|
holger@0
|
122
|
holger@0
|
123
|
holger@0
|
124 assert(length(f0Idcs_DB) == size(spectra_DB,2), 'number of values in ''f0Idcs_DB'' must be the same as number of columns in W_DB');
|
holger@0
|
125 assert(length(f0Idcs_data) == size(spectra_data,2), 'number of values in ''f0Idcs_data'' must be the same as number of columns in W_data');
|
holger@0
|
126 assert(size(spectra_DB,1) == size(spectra_data,1), 'number of frequency bins (rows) in W_DB and W_data must be the same');
|
holger@0
|
127
|
holger@0
|
128 % FIXME: check that inputs are valid!
|
holger@0
|
129
|
holger@0
|
130 %% set member variables
|
holger@0
|
131 obj.f0Idcs_DB = f0Idcs_DB;
|
holger@0
|
132 obj.f0Idcs_data = f0Idcs_data;
|
holger@0
|
133 obj.spectra_DB = spectra_DB;
|
holger@0
|
134 obj.spectra_data = spectra_data;
|
holger@0
|
135
|
holger@0
|
136 obj.maxNumFreqs = size(spectra_DB, 1);
|
holger@0
|
137 obj.numF0Idcs_DB = size(spectra_DB, 2);
|
holger@0
|
138 obj.numF0Idcs_data = size(spectra_data,2);
|
holger@0
|
139
|
holger@0
|
140 [obj.commonF0Idcs, obj.commonF0IdcsIdcs_DB, obj.commonF0IdcsIdcs_data] = intersect(f0Idcs_DB, f0Idcs_data);
|
holger@0
|
141 obj.numCommonShifts = length(obj.commonF0IdcsIdcs_DB);
|
holger@0
|
142
|
holger@0
|
143
|
holger@0
|
144 %% reduce spectra to partial amplitudes only
|
holger@0
|
145 obj.W_DB = obj.noteSpec2partialSpec(spectra_DB, f0Idcs_DB, numBinsPerSemitone);
|
holger@0
|
146 obj.W_data = obj.noteSpec2partialSpec(spectra_data, f0Idcs_data, numBinsPerSemitone);
|
holger@0
|
147
|
holger@0
|
148 %% adjust amplitudes in database spectra at common pitches
|
holger@0
|
149 obj.W_DB(:, obj.commonF0IdcsIdcs_DB) = obj.adjustPartialPositions(obj.W_DB(:, obj.commonF0IdcsIdcs_DB), ...
|
holger@0
|
150 obj.W_data(:, obj.commonF0IdcsIdcs_data), ...
|
holger@0
|
151 obj.commonF0Idcs, numBinsPerSemitone);
|
holger@0
|
152
|
holger@0
|
153
|
holger@0
|
154 %% find zero-entries in W_data and W_data_est
|
holger@0
|
155 obj.zeroIdcsW_data = (obj.W_data(:,obj.commonF0IdcsIdcs_data) == 0);
|
holger@0
|
156 obj.zeroIdcsW_data_est = (obj.W_DB(:,obj.commonF0IdcsIdcs_DB) == 0); % zero where W_DB is zero (see computeW_data_est)
|
holger@0
|
157 obj.zeroIdcsH = (sum(obj.W_data,2) == 0) | (sum(obj.W_DB(:,obj.commonF0IdcsIdcs_DB),2) == 0);
|
holger@0
|
158
|
holger@0
|
159 assert(ischar(costFctName), ~any(strcmpi(costFctName, obj.costFctNames)), ...
|
holger@0
|
160 'Argument ''costFctName'' must be a string, and must match one of the implemented cost function names.');
|
holger@0
|
161 obj.costFctName = costFctName;
|
holger@0
|
162
|
holger@0
|
163
|
holger@0
|
164 %% set beta & cost function name
|
holger@0
|
165 switch obj.costFctName
|
holger@0
|
166 case 'LS'
|
holger@0
|
167 obj.beta = 2;
|
holger@0
|
168 obj.costFctName = 'BD';
|
holger@0
|
169 case 'KL'
|
holger@0
|
170 obj.beta = 1;
|
holger@0
|
171 obj.costFctName = 'BD';
|
holger@0
|
172 case 'IS'
|
holger@0
|
173 obj.beta = 0;
|
holger@0
|
174 obj.costFctName = 'BD';
|
holger@0
|
175 case 'BD'
|
holger@0
|
176 %check if beta was set
|
holger@0
|
177 if isempty(varargin) % FIXME: if more optional arguments are added later, use MATLAB's inputparser
|
holger@0
|
178 error('When ''BD'' is used as the cost function, beta needs to be set.');
|
holger@0
|
179 elseif ~strcmp(varargin{1}, 'beta')
|
holger@0
|
180 error('Name/value pair for ''beta'' not found.')
|
holger@0
|
181 end
|
holger@0
|
182 beta = varargin{2};
|
holger@0
|
183 validateattributes(beta, {'numeric'}, {'scalar', 'real', 'finite', 'nonnan'}, 'CSourceFilter', 'beta', 5)
|
holger@0
|
184 obj.beta = beta;
|
holger@0
|
185 end
|
holger@0
|
186
|
holger@0
|
187 %% initialise h
|
holger@0
|
188 obj.h = zeros(obj.maxNumFreqs,1);
|
holger@0
|
189 obj.h(~obj.zeroIdcsH) = 1;
|
holger@0
|
190
|
holger@0
|
191 %% compute initial WEst
|
holger@0
|
192 obj = computeW_data_est(obj);
|
holger@0
|
193
|
holger@0
|
194 end
|
holger@0
|
195
|
holger@0
|
196
|
holger@0
|
197 function obj = updateH(obj)
|
holger@0
|
198 % updateH - perform single update of filter curve ''h''
|
holger@0
|
199 %
|
holger@0
|
200 % myObj = myObj.updateH applies the update functions to the
|
holger@0
|
201 % filter curve ''h''.
|
holger@0
|
202
|
holger@0
|
203 %% get spectra at common f0 indices
|
holger@0
|
204 W_data = obj.W_data(:, obj.commonF0IdcsIdcs_data);
|
holger@0
|
205 W_DB = obj.W_DB(:, obj.commonF0IdcsIdcs_DB);
|
holger@0
|
206 W_data_est = obj.W_data_est;
|
holger@0
|
207
|
holger@0
|
208
|
holger@0
|
209 %% compute W_data * W_data_est^(beta-2)
|
holger@0
|
210 nomMatrix = W_data .* W_data_est .^ (obj.beta-2);
|
holger@0
|
211
|
holger@0
|
212 % fix divide by 0
|
holger@0
|
213 if obj.beta < 2 % if beta < 2, exponent of WEst^(beta-2) is negative -> division
|
holger@0
|
214 maxRatio = max(max(nomMatrix( ~obj.zeroIdcsW_data_est )));
|
holger@0
|
215 nomMatrix(obj.zeroIdcsW_data & obj.zeroIdcsW_data_est) = 1;
|
holger@0
|
216 nomMatrix(~obj.zeroIdcsW_data & obj.zeroIdcsW_data_est) = maxRatio;
|
holger@0
|
217 end
|
holger@0
|
218
|
holger@0
|
219 %% compute W_data^(beta-1)
|
holger@0
|
220 denomMatrix = W_data_est .^ (obj.beta-1);
|
holger@0
|
221
|
holger@0
|
222 % fix divide by 0
|
holger@0
|
223 if obj.beta < 1 % if beta < 1, exponent of WEst^(beta-1) is negative -> division
|
holger@0
|
224 maxRatio = max(max(denomMatrix(~obj.zeroIdcsW_data_est)));
|
holger@0
|
225 denomMatrix(obj.zeroIdcsW_data_est) = maxRatio;
|
holger@0
|
226 end
|
holger@0
|
227
|
holger@0
|
228 %% multiply by W_DB
|
holger@0
|
229 nomMatrix = nomMatrix .* W_DB;
|
holger@0
|
230 denomMatrix = denomMatrix .* W_DB;
|
holger@0
|
231
|
holger@0
|
232
|
holger@0
|
233 %% compute nominator and denominator
|
holger@0
|
234 nom = sum(nomMatrix, 2);
|
holger@0
|
235 denom = sum(denomMatrix, 2);
|
holger@0
|
236
|
holger@0
|
237 %% compute ratio
|
holger@0
|
238 ratio = nom ./ denom;
|
holger@0
|
239 ratio((nom==0) & (denom==0)) = 1;
|
holger@0
|
240 ratio((nom~=0) & (denom==0)) = max(ratio);
|
holger@0
|
241
|
holger@0
|
242 %% apply update
|
holger@0
|
243 obj.h(~obj.zeroIdcsH) = obj.h(~obj.zeroIdcsH) .* ratio(~obj.zeroIdcsH);
|
holger@0
|
244
|
holger@0
|
245 %% recompute WEst
|
holger@0
|
246 obj = computeW_data_est(obj);
|
holger@0
|
247 end
|
holger@0
|
248
|
holger@0
|
249
|
holger@0
|
250 function [spectra shiftVals] = estimateSpectra(obj, shiftVals)
|
holger@0
|
251 % estimateSpectra - computes basis functions from the current
|
holger@0
|
252 % estimates for e and h
|
holger@0
|
253 %
|
holger@0
|
254 % [spectra f0Idcs] = myObj.estimateSpectra(f0Idcs) estimates the
|
holger@0
|
255 % spectra at the f0 indices provided by ''f0Idcs'' by applying
|
holger@0
|
256 % the current estimate of the filter curve to the spectra in
|
holger@0
|
257 % ''spectra_DB'' (see constructor). Spectra are only returned
|
holger@0
|
258 % for those f0Idcs that exist in ''f0Idcs_DB'' specified in the
|
holger@0
|
259 % constructor. The output is a matrix ''spectra'' containing the
|
holger@0
|
260 % estimated spectra and a vector ''f0Idcs'' containing the
|
holger@0
|
261 % corresponding f0 indices.
|
holger@0
|
262
|
holger@0
|
263 %FIXME: check that input variable 'shiftVals' is correct
|
holger@0
|
264
|
holger@0
|
265 % find values in shiftVals that also exist in obj.f0Idcs_DB
|
holger@0
|
266 [commonShiftVals, shiftIdcs_DB, dummy] = intersect(obj.f0Idcs_DB, shiftVals);
|
holger@0
|
267 numCommonShiftVals = length(commonShiftVals);
|
holger@0
|
268
|
holger@0
|
269 h = obj.getSmoothedH();
|
holger@0
|
270 %h = obj.getH();
|
holger@0
|
271 spectra = obj.spectra_DB(:,shiftIdcs_DB) .* repmat(h, 1, numCommonShiftVals);
|
holger@0
|
272 end
|
holger@0
|
273
|
holger@0
|
274 function h = getH(obj)
|
holger@0
|
275 % getH - get filter curve ''h''
|
holger@0
|
276 %
|
holger@0
|
277 % myH = myObj.getH() returns the member variable ''h''.
|
holger@0
|
278
|
holger@0
|
279 h = obj.h;
|
holger@0
|
280 end
|
holger@0
|
281
|
holger@0
|
282 function h = getSmoothedH(obj)
|
holger@0
|
283 % getH - get smoothed version of filter curve ''h''
|
holger@0
|
284 %
|
holger@0
|
285 % myH = myObj.getSmoothedH() returns a smoothed version of the
|
holger@0
|
286 % filter curve ''h''. Smoothing is done by applying the discrete
|
holger@0
|
287 % cepstrum spectral envelope algorithm from Diemo Schwartz to
|
holger@0
|
288 % the filter curve ''h''.
|
holger@0
|
289
|
holger@0
|
290 nonZeroFreqIdcsH = find(~obj.zeroIdcsH);
|
holger@0
|
291
|
holger@0
|
292 % select nonzero entries from h
|
holger@0
|
293 h = obj.h;
|
holger@0
|
294 h_nonzero = h(nonZeroFreqIdcsH);
|
holger@0
|
295 h_nonzero_DB = 20*log10(h_nonzero);
|
holger@0
|
296
|
holger@0
|
297
|
holger@0
|
298 % correct outliers that are more than 10 dB above or below median
|
holger@0
|
299 medianInDB = median(h_nonzero_DB);
|
holger@0
|
300
|
holger@0
|
301 idcs = h_nonzero_DB > medianInDB + obj.maxDevFromMedianInDB;
|
holger@0
|
302 h_nonzero(idcs) = 10^( (medianInDB + obj.maxDevFromMedianInDB)/20 );
|
holger@0
|
303
|
holger@0
|
304 idcs = h_nonzero_DB < medianInDB - obj.maxDevFromMedianInDB;
|
holger@0
|
305 h_nonzero(idcs) = 10^( (medianInDB - obj.maxDevFromMedianInDB)/20 );
|
holger@0
|
306
|
holger@0
|
307
|
holger@0
|
308 % setup vector containing frequencies for cosine approx.
|
holger@0
|
309 w = (1:obj.maxNumFreqs)' / obj.maxNumFreqs * pi;
|
holger@0
|
310
|
holger@0
|
311 % select w at nonzero entries of h
|
holger@0
|
312 w_nonzero = w(nonZeroFreqIdcsH);
|
holger@0
|
313
|
holger@0
|
314 % copy first and last nonzero entry to boundaries
|
holger@0
|
315 if nonZeroFreqIdcsH(1) ~= 1
|
holger@0
|
316 h_nonzero = [h_nonzero(1); h_nonzero];
|
holger@0
|
317 w_nonzero = [w(1); w_nonzero];
|
holger@0
|
318 end
|
holger@0
|
319 if nonZeroFreqIdcsH(end) ~= obj.maxNumFreqs
|
holger@0
|
320 h_nonzero = [h_nonzero; h_nonzero(end)];
|
holger@0
|
321 w_nonzero = [w_nonzero; w(end)];
|
holger@0
|
322 end
|
holger@0
|
323
|
holger@0
|
324
|
holger@0
|
325 % apply cosine approximation to h (discrete cepstrum)
|
holger@0
|
326 coeffs = dceps(h_nonzero, w_nonzero, obj.numCepstralCoeffs, obj.regularisationParam);
|
holger@0
|
327 h = idceps(coeffs, w);
|
holger@0
|
328
|
holger@0
|
329 end
|
holger@0
|
330
|
holger@0
|
331 function obj = setH(obj, h)
|
holger@0
|
332 % setH - set member variable h
|
holger@0
|
333 %
|
holger@0
|
334 % myObj = myObj.setH(myH) sets the member variable h to myH.
|
holger@0
|
335 % myH must be a non-negative column vector of length [number of
|
holger@0
|
336 % frequencies].
|
holger@0
|
337
|
holger@0
|
338 obj.h = h;
|
holger@0
|
339 obj = computeW_data_est(obj);
|
holger@0
|
340 end
|
holger@0
|
341
|
holger@0
|
342 function betaDiv = compBetaDivergence(obj)
|
holger@0
|
343 % compBetaDivergence - computes beta divergence based on the
|
holger@0
|
344 % current estiates
|
holger@0
|
345 %
|
holger@0
|
346 % betaDiv = myObj.compBetaDivergence() returns the
|
holger@0
|
347 % beta-divergence between the instrument spectra from the
|
holger@0
|
348 % recording and the adapted database spectra based on the value
|
holger@0
|
349 % for beta specified by the cost function in the constructor.
|
holger@0
|
350
|
holger@0
|
351 W_data = obj.W_data(~obj.zeroIdcsW_data_est);
|
holger@0
|
352 W_data_est = obj.W_data_est(~obj.zeroIdcsW_data_est);
|
holger@0
|
353
|
holger@0
|
354 switch obj.beta
|
holger@0
|
355 case 0
|
holger@0
|
356 betaDivMat = W_data ./ W_data_est - log(W_data ./ W_data_est) - 1;
|
holger@0
|
357
|
holger@0
|
358 case 1
|
holger@0
|
359 betaDivMat = W_data .* log(W_data ./ W_data_est) + W_data - W_data_est;
|
holger@0
|
360
|
holger@0
|
361 otherwise
|
holger@0
|
362 betaDivMat = (W_data .^ obj.beta) / (obj.beta * (obj.beta-1)) ...
|
holger@0
|
363 + (W_data_est .^ obj.beta) / obj.beta ...
|
holger@0
|
364 - (W_data .* (W_data_est .^ (obj.beta-1))) / (obj.beta-1);
|
holger@0
|
365 end
|
holger@0
|
366
|
holger@0
|
367 betaDiv = sum(betaDivMat(:));
|
holger@0
|
368 end
|
holger@0
|
369
|
holger@0
|
370 end % methods
|
holger@0
|
371
|
holger@0
|
372
|
holger@0
|
373 methods (Access = private)
|
holger@0
|
374
|
holger@0
|
375 function obj = computeW_data_est(obj)
|
holger@0
|
376 % computes basis functions from the current estimates for s, e and h
|
holger@0
|
377
|
holger@0
|
378 if ~isempty(obj.h)
|
holger@0
|
379 obj.W_data_est = obj.W_DB(:,obj.commonF0IdcsIdcs_DB) .* repmat(obj.h, 1, obj.numCommonShifts);
|
holger@0
|
380 end
|
holger@0
|
381 end
|
holger@0
|
382
|
holger@0
|
383 end % methods (Access = private)
|
holger@0
|
384
|
holger@0
|
385
|
holger@0
|
386 methods (Access = private, Static)
|
holger@0
|
387
|
holger@0
|
388 function partialSpectra = noteSpec2partialSpec(noteSpectra, f0Idcs, numBinsPerSemitone)
|
holger@0
|
389 % goes through all note spectra, extracts the partial amplitudes
|
holger@0
|
390 % and writes them to their absolute frequency positions
|
holger@0
|
391
|
holger@0
|
392 % initialize matrix for result
|
holger@0
|
393 [numFreqs numPitches] = size(noteSpectra);
|
holger@0
|
394 partialSpectra = zeros(numFreqs, numPitches);
|
holger@0
|
395
|
holger@0
|
396 % get (ideal) relative partial positions
|
holger@0
|
397 maxNumPartials = floor(freqIdx2PartialIdx(numFreqs, numBinsPerSemitone));
|
holger@0
|
398 relF0IdcsOfPartials = partialIdx2FreqIdx((1:maxNumPartials)', numBinsPerSemitone);
|
holger@0
|
399 meansF0Idcs = geomean( [relF0IdcsOfPartials(1:end-1)'; relF0IdcsOfPartials(2:end)'] )';
|
holger@0
|
400 relLowerBoundOfPartials = [1; floor(meansF0Idcs)+1];
|
holger@0
|
401 relUpperBoundOfPartials = [floor(meansF0Idcs); numFreqs];
|
holger@0
|
402
|
holger@0
|
403 % go through all spectra
|
holger@0
|
404 for pitchIdx = 1:numPitches
|
holger@0
|
405
|
holger@0
|
406 currF0Idx = f0Idcs(pitchIdx);
|
holger@0
|
407 currNumPartials = floor(freqIdx2PartialIdx(numFreqs-currF0Idx+1, numBinsPerSemitone));
|
holger@0
|
408
|
holger@0
|
409 % go through partials
|
holger@0
|
410 for partialIdx = 1:currNumPartials
|
holger@0
|
411
|
holger@0
|
412 % find maximum with partial range
|
holger@0
|
413 lowerBound = currF0Idx-1 + relLowerBoundOfPartials(partialIdx);
|
holger@0
|
414 upperBound = min(currF0Idx-1 + relUpperBoundOfPartials(partialIdx), numFreqs);
|
holger@0
|
415 [maxAmpl maxIdx] = max(noteSpectra(lowerBound:upperBound, pitchIdx));
|
holger@0
|
416
|
holger@0
|
417 % write to result matrix
|
holger@0
|
418 partialSpectra(lowerBound-1+maxIdx, pitchIdx) = maxAmpl;
|
holger@0
|
419
|
holger@0
|
420 end
|
holger@0
|
421 end
|
holger@0
|
422 end % noteSpec2partialSpec
|
holger@0
|
423
|
holger@0
|
424
|
holger@0
|
425 function W_DB = adjustPartialPositions(W_DB, W_data, f0Idcs, numBinsPerSemitone)
|
holger@0
|
426 % adjust the positions of the partials in W_DB to those in W_data
|
holger@0
|
427
|
holger@0
|
428 assert(size(W_DB,2) == size(W_data,2), '''W_DB'' and ''W_data'' must contain the same number of columns');
|
holger@0
|
429 assert(length(f0Idcs) == size(W_DB,2), 'Number of elements in ''f0Idcs'' must be equal to number of columns in ''W_DB''');
|
holger@0
|
430
|
holger@0
|
431 [numFreqs numPitches] = size(W_DB);
|
holger@0
|
432
|
holger@0
|
433 % get (ideal) relative partial positions
|
holger@0
|
434 maxNumPartials = floor(freqIdx2PartialIdx(numFreqs, numBinsPerSemitone));
|
holger@0
|
435 relF0IdcsOfPartials = partialIdx2FreqIdx((1:maxNumPartials)', numBinsPerSemitone);
|
holger@0
|
436 meansF0Idcs = geomean( [relF0IdcsOfPartials(1:end-1)'; relF0IdcsOfPartials(2:end)'] )';
|
holger@0
|
437 relLowerBoundOfPartials = [-ceil(numBinsPerSemitone/2); floor(meansF0Idcs)+1]; % make 1st bound -numBinsPerSemitone/2 to allow 1st partial to deviate below ideal position
|
holger@0
|
438 relUpperBoundOfPartials = [floor(meansF0Idcs); numFreqs];
|
holger@0
|
439
|
holger@0
|
440 % go through all spectra
|
holger@0
|
441 for pitchIdx = 1:numPitches
|
holger@0
|
442
|
holger@0
|
443 currF0Idx = f0Idcs(pitchIdx);
|
holger@0
|
444 currNumPartials = compNumPartials(numFreqs-currF0Idx+1, numBinsPerSemitone);
|
holger@0
|
445
|
holger@0
|
446 % go through partials
|
holger@0
|
447 for partialIdx = 1:currNumPartials
|
holger@0
|
448
|
holger@0
|
449 % compute bounds for partial range
|
holger@0
|
450 lowerBound = max(currF0Idx-1 + relLowerBoundOfPartials(partialIdx), 1);
|
holger@0
|
451 upperBound = min(currF0Idx-1 + relUpperBoundOfPartials(partialIdx), numFreqs);
|
holger@0
|
452
|
holger@0
|
453 % get indices of partial in both W_DB and W_data
|
holger@0
|
454 idx_DB = find(W_DB(lowerBound:upperBound, pitchIdx));
|
holger@0
|
455 idx_data = find(W_data(lowerBound:upperBound, pitchIdx));
|
holger@0
|
456
|
holger@0
|
457 % set partial amplitude in W_DB to frequency index of W_data
|
holger@0
|
458 partAmpl = W_DB(lowerBound-1+idx_DB, pitchIdx);
|
holger@0
|
459 W_DB(lowerBound-1+idx_DB, pitchIdx) = 0;
|
holger@0
|
460 W_DB(lowerBound-1+idx_data, pitchIdx) = partAmpl;
|
holger@0
|
461 end
|
holger@0
|
462 end
|
holger@0
|
463 end % adjustPartialPositions
|
holger@0
|
464 end
|
holger@0
|
465
|
holger@0
|
466 % methods (Static)
|
holger@0
|
467 %
|
holger@0
|
468 % function Xshift = shiftSpectra(X, shiftVals)
|
holger@0
|
469 % % shifts each spectrum (column in X) down by the amount specified in
|
holger@0
|
470 % % shiftVals
|
holger@0
|
471 %
|
holger@0
|
472 % assert(size(X,2) == length(shiftVals), 'number values in ''shiftVals'' must be the same as number of columns in ''X''');
|
holger@0
|
473 %
|
holger@0
|
474 % [maxNumFreqs numShifts] = size(X);
|
holger@0
|
475 % Xshift = zeros(maxNumFreqs, numShifts);
|
holger@0
|
476 %
|
holger@0
|
477 % for pitchIdx = 1:numShifts
|
holger@0
|
478 % phi = shiftVals(pitchIdx);
|
holger@0
|
479 % Xshift(1:maxNumFreqs-phi, pitchIdx) = X(phi+1:maxNumFreqs, pitchIdx);
|
holger@0
|
480 % end
|
holger@0
|
481 % end
|
holger@0
|
482 % end % methods (Static)
|
holger@0
|
483
|
holger@0
|
484 end |