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