annotate 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
rev   line source
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