annotate AudioDegradationToolbox/degradationUnits/degradationUnit_adaptiveEqualizer.m @ 28:76f45f5c9afd DoP tip

- added units * adaptiveEqualizer * applyMfccMeanAdaption - added corresponding data files for presets - modified applyImpulseReponse to use the estimated average group delay to adjust the output audio and keep the timestamps as is (was vice versa before) - added new units demos, incl one for applyLowpass
author SebastianEwert
date Tue, 21 Jan 2014 18:08:28 +0000
parents
children
rev   line source
SebastianEwert@28 1 function [f_audio_out,timepositions_afterDegr] = degradationUnit_adaptiveEqualizer(f_audio, samplingFreq, timepositions_beforeDegr, parameter)
SebastianEwert@28 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SebastianEwert@28 3 % Name: degradationUnit_adaptiveEqualizer
SebastianEwert@28 4 % Date of Revision: 2013-01-23
SebastianEwert@28 5 % Programmer: Sebastian Ewert
SebastianEwert@28 6 %
SebastianEwert@28 7 % Description:
SebastianEwert@28 8 % - designs a filter such that the mean spectrum of f_audio becomes similar
SebastianEwert@28 9 % to a given mean spectrum
SebastianEwert@28 10 % - mean spectra are specified using a decibel scale, i.e. if x is a magnitude
SebastianEwert@28 11 % spectrum, then apply x -> 20*log10(x)
SebastianEwert@28 12 % - there are four ways to specify the destination mean spectrum: 1. by
SebastianEwert@28 13 % loading example files provided with the toolbox, 2. by using specific
SebastianEwert@28 14 % noise "color" profiles, 3. by providing the destination mean spectrum
SebastianEwert@28 15 % using the parameter destMagFreqResp, 4. by providing audio data from
SebastianEwert@28 16 % which the destination mean spectrum is computed.
SebastianEwert@28 17 % - if mean spectra are computed, this is done by computing a magnitude
SebastianEwert@28 18 % spectrogram, deriving the corresponding values in dB, and then averaging
SebastianEwert@28 19 % over all time frames.
SebastianEwert@28 20 % - the same is done for f_audio and the two mean spectral vectors are
SebastianEwert@28 21 % compared
SebastianEwert@28 22 % - Then a filter is designed such that f_audio's mean spectral vector
SebastianEwert@28 23 % becomes similar to destMagFreqResp
SebastianEwert@28 24 % - filtering is done using a linear-phase FIR filter
SebastianEwert@28 25 % - this unit normalizes the output
SebastianEwert@28 26 %
SebastianEwert@28 27 % Notes:
SebastianEwert@28 28 % - note that the mean spectrum is often a good approximation of what is
SebastianEwert@28 29 % sometimes referred to as the mean spectral shape (however, this term is not
SebastianEwert@28 30 % defined for general sound recordings). In this sense, the function
SebastianEwert@28 31 % modifies the mean spectral shape to the desired one.
SebastianEwert@28 32 %
SebastianEwert@28 33 % Input:
SebastianEwert@28 34 % f_audio - audio signal \in [-1,1]^{NxC} with C being the number of
SebastianEwert@28 35 % channels
SebastianEwert@28 36 % samplingFreq - sampling frequency of f_audio
SebastianEwert@28 37 % timepositions_beforeDegr - some degradations delay the input signal. If
SebastianEwert@28 38 % some points in time are given via this
SebastianEwert@28 39 % parameter, timepositions_afterDegr will
SebastianEwert@28 40 % return the corresponding positions in the
SebastianEwert@28 41 % output. Set to [] if unavailable. Set f_audio
SebastianEwert@28 42 % and samplingFreq to [] to compute only
SebastianEwert@28 43 % timepositions_afterDegr.
SebastianEwert@28 44 %
SebastianEwert@28 45 % Input (optional): parameter
SebastianEwert@28 46 % .loadInternalMagFreqResp=1 - loads one of the destMagFreqResp provided
SebastianEwert@28 47 % by the toolbox
SebastianEwert@28 48 % .internalMagFreqResp='Beatles_NorwegianWood'
SebastianEwert@28 49 % .computeMagFreqRespFromAudio - computes destMagFreqResp from given
SebastianEwert@28 50 % audio data
SebastianEwert@28 51 % .computeMagFreqRespFromAudio_audioData
SebastianEwert@28 52 % - audio data for .computeMagFreqRespFromAudio
SebastianEwert@28 53 % .computeMagFreqRespFromAudio_sf - sampl freq for .computeMagFreqRespFromAudio
SebastianEwert@28 54 % .destMagFreqResp = [] - in db. See above.
SebastianEwert@28 55 % .destMagFreqResp_freqs = [] - must have same length as destMagFreqResp.
SebastianEwert@28 56 % In Hertz
SebastianEwert@28 57 % .fftLength = 2 ^ nextpow2(0.02 * samplingFreq); - fft length to
SebastianEwert@28 58 % calculate spectrogram of f_audio.
SebastianEwert@28 59 %
SebastianEwert@28 60 % Output:
SebastianEwert@28 61 % f_audio_out - audio signal \in [-1,1]^{NxC} with C being the number
SebastianEwert@28 62 % of channels
SebastianEwert@28 63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SebastianEwert@28 64
SebastianEwert@28 65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SebastianEwert@28 66 % Audio Degradation Toolbox
SebastianEwert@28 67 %
SebastianEwert@28 68 % Centre for Digital Music, Queen Mary University of London.
SebastianEwert@28 69 % This file copyright 2013 Sebastian Ewert, Matthias Mauch and QMUL.
SebastianEwert@28 70 %
SebastianEwert@28 71 % This program is free software; you can redistribute it and/or
SebastianEwert@28 72 % modify it under the terms of the GNU General Public License as
SebastianEwert@28 73 % published by the Free Software Foundation; either version 2 of the
SebastianEwert@28 74 % License, or (at your option) any later version. See the file
SebastianEwert@28 75 % COPYING included with this distribution for more information.
SebastianEwert@28 76 %
SebastianEwert@28 77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SebastianEwert@28 78
SebastianEwert@28 79 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SebastianEwert@28 80 % Check parameters
SebastianEwert@28 81 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SebastianEwert@28 82 if nargin<4
SebastianEwert@28 83 parameter=[];
SebastianEwert@28 84 end
SebastianEwert@28 85 if nargin<3
SebastianEwert@28 86 timepositions_beforeDegr=[];
SebastianEwert@28 87 end
SebastianEwert@28 88 if nargin<2
SebastianEwert@28 89 error('Please specify input data');
SebastianEwert@28 90 end
SebastianEwert@28 91
SebastianEwert@28 92 if isfield(parameter,'loadInternalMagFreqResp')==0
SebastianEwert@28 93 parameter.loadInternalMagFreqResp = 1;
SebastianEwert@28 94 end
SebastianEwert@28 95 if isfield(parameter,'loadNoiseColorPreset')==0
SebastianEwert@28 96 parameter.loadNoiseColorPreset = 0;
SebastianEwert@28 97 end
SebastianEwert@28 98 if isfield(parameter,'computeMagFreqRespFromAudio')==0
SebastianEwert@28 99 parameter.computeMagFreqRespFromAudio = 0;
SebastianEwert@28 100 end
SebastianEwert@28 101
SebastianEwert@28 102 if isfield(parameter,'internalMagFreqResp')==0
SebastianEwert@28 103 parameter.internalMagFreqResp = 'Beethoven_Appasionata_Rwc';
SebastianEwert@28 104 end
SebastianEwert@28 105 if isfield(parameter,'noiseColorPreset')==0
SebastianEwert@28 106 parameter.noiseColorPreset = 'pink';
SebastianEwert@28 107 end
SebastianEwert@28 108 if isfield(parameter,'computeMagFreqRespFromAudio_audioData')==0
SebastianEwert@28 109 parameter.computeMagFreqRespFromAudio_audioData = [];
SebastianEwert@28 110 end
SebastianEwert@28 111 if isfield(parameter,'computeMagFreqRespFromAudio_sf')==0
SebastianEwert@28 112 parameter.computeMagFreqRespFromAudio_sf = [];
SebastianEwert@28 113 end
SebastianEwert@28 114 if isfield(parameter,'destMagFreqResp')==0
SebastianEwert@28 115 parameter.destMagFreqResp = [];
SebastianEwert@28 116 end
SebastianEwert@28 117 if isfield(parameter,'destMagFreqResp_freqs')==0
SebastianEwert@28 118 parameter.destMagFreqResp_freqs = [];
SebastianEwert@28 119 end
SebastianEwert@28 120 if isfield(parameter,'fftLength')==0
SebastianEwert@28 121 parameter.fftLength = 2 ^ nextpow2(0.02 * samplingFreq);
SebastianEwert@28 122 end
SebastianEwert@28 123 if isfield(parameter,'filterOrder')==0
SebastianEwert@28 124 parameter.filterOrder = round(parameter.fftLength/2);
SebastianEwert@28 125 end
SebastianEwert@28 126 if isfield(parameter,'visualizations')==0
SebastianEwert@28 127 parameter.visualizations = 0;
SebastianEwert@28 128 end
SebastianEwert@28 129
SebastianEwert@28 130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SebastianEwert@28 131 % Main program
SebastianEwert@28 132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SebastianEwert@28 133 if isempty(f_audio)
SebastianEwert@28 134 % we design a linear-phase filter. Such filters have the property that
SebastianEwert@28 135 % the group delay for every frequency is always equal to
SebastianEwert@28 136 % parameter.filterOrder/2. Therefore, although we built a signal
SebastianEwert@28 137 % adaptive filter, we can adjust for that delay. That means that the
SebastianEwert@28 138 % timepositions_beforeDegr don't require any adjustment.
SebastianEwert@28 139 timepositions_afterDegr = timepositions_beforeDegr;
SebastianEwert@28 140 return;
SebastianEwert@28 141 end
SebastianEwert@28 142
SebastianEwert@28 143 % load/compute destMagFreqResp
SebastianEwert@28 144 [destMagFreqResp,destMagFreqResp_freqs] = internal_getDestMagFreqResp(parameter);
SebastianEwert@28 145
SebastianEwert@28 146 % compute mean spectral vector for f_audio
SebastianEwert@28 147 [meanMagSpec,meanMagSpec_freqs] = internal_computeMeanSpectralVector(f_audio,samplingFreq,parameter.fftLength);
SebastianEwert@28 148 meanMagSpec = internal_standardizeMagFreqResp(meanMagSpec);
SebastianEwert@28 149
SebastianEwert@28 150 % compute magnitude response for the filter to be designed
SebastianEwert@28 151 destMagFreqResp_org = destMagFreqResp;
SebastianEwert@28 152 destMagFreqResp_freqs_org = destMagFreqResp_freqs;
SebastianEwert@28 153 if ~((length(destMagFreqResp_freqs)==length(meanMagSpec_freqs)) && (all(meanMagSpec_freqs(:) == destMagFreqResp_freqs(:))))
SebastianEwert@28 154 % in this case we interpolate the frequency response using a
SebastianEwert@28 155 % spline interpolation
SebastianEwert@28 156 destMagFreqResp = spline(destMagFreqResp,destMagFreqResp_freqs,meanMagSpec_freqs);
SebastianEwert@28 157 end
SebastianEwert@28 158 filter_magFreqResp = destMagFreqResp(:) - meanMagSpec(:);
SebastianEwert@28 159
SebastianEwert@28 160 % design filter (fir2 is linear phase)
SebastianEwert@28 161 filter_magFreqResp_linear = 10 .^ (filter_magFreqResp/20);
SebastianEwert@28 162 b = fir2(parameter.filterOrder,meanMagSpec_freqs/(samplingFreq/2),filter_magFreqResp_linear);
SebastianEwert@28 163
SebastianEwert@28 164 % apply filter
SebastianEwert@28 165 parameterApplyImpulseResponse.loadInternalIR = 0;
SebastianEwert@28 166 parameterApplyImpulseResponse.impulseResponse = b;
SebastianEwert@28 167 parameterApplyImpulseResponse.impulseResponseSampFreq = samplingFreq;
SebastianEwert@28 168 parameterApplyImpulseResponse.normalizeOutputAudio = 1;
SebastianEwert@28 169 parameterApplyImpulseResponse.averageGroupDelayOfFilter = round(parameter.filterOrder/2);
SebastianEwert@28 170 [f_audio_out,timepositions_afterDegr] = degradationUnit_applyImpulseResponse(f_audio, samplingFreq, timepositions_beforeDegr, parameterApplyImpulseResponse);
SebastianEwert@28 171
SebastianEwert@28 172 if parameter.visualizations
SebastianEwert@28 173 fvtool(b,1);
SebastianEwert@28 174
SebastianEwert@28 175 [meanMagSpecOut,meanMagSpecOut_freqs] = internal_computeMeanSpectralVector(f_audio_out,samplingFreq,parameter.fftLength);
SebastianEwert@28 176 meanMagSpecOut = internal_standardizeMagFreqResp(meanMagSpecOut);
SebastianEwert@28 177
SebastianEwert@28 178 figure;
SebastianEwert@28 179 plot(destMagFreqResp_freqs_org,destMagFreqResp_org,'y');
SebastianEwert@28 180 hold on;
SebastianEwert@28 181 plot(meanMagSpecOut_freqs,meanMagSpecOut,'k');
SebastianEwert@28 182 title('Comparison: destMagFreqResp(y) and mean spectral vector of output(k)')
SebastianEwert@28 183 end
SebastianEwert@28 184
SebastianEwert@28 185
SebastianEwert@28 186 end
SebastianEwert@28 187
SebastianEwert@28 188 function [f_meanmagspec_db,freqs] = internal_computeMeanSpectralVector(f_audio,fs,fftLength)
SebastianEwert@28 189
SebastianEwert@28 190 f_audio = mean(f_audio,2);
SebastianEwert@28 191
SebastianEwert@28 192 [f_spec,freqs,time] = spectrogram(f_audio,hanning(fftLength),fftLength/2,fftLength,fs);
SebastianEwert@28 193
SebastianEwert@28 194 f_magspec_db = 20 * log10(abs(f_spec));
SebastianEwert@28 195
SebastianEwert@28 196 f_magspec_db(:,isinf(sum(abs(f_magspec_db),1))) = []; % ignore columns with -inf/inf entries
SebastianEwert@28 197 f_magspec_db(:,isnan(sum(abs(f_magspec_db),1))) = [];
SebastianEwert@28 198
SebastianEwert@28 199 f_meanmagspec_db = mean(f_magspec_db,2);
SebastianEwert@28 200
SebastianEwert@28 201 end
SebastianEwert@28 202
SebastianEwert@28 203 function magFreqResp = internal_standardizeMagFreqResp(magFreqResp)
SebastianEwert@28 204
SebastianEwert@28 205 temp = magFreqResp(~isinf(magFreqResp));
SebastianEwert@28 206 temp = temp(~isnan(magFreqResp));
SebastianEwert@28 207 maxRobust = max(temp);
SebastianEwert@28 208
SebastianEwert@28 209 magFreqResp = magFreqResp - maxRobust;
SebastianEwert@28 210
SebastianEwert@28 211 magFreqResp(magFreqResp > 0) = 0; % remaining positive inf
SebastianEwert@28 212 magFreqResp(magFreqResp < -80) = -80; % remaining positive inf
SebastianEwert@28 213
SebastianEwert@28 214 end
SebastianEwert@28 215
SebastianEwert@28 216 function [destMagFreqResp,destMagFreqResp_freqs,fftLength] = internal_getDestMagFreqResp(parameter)
SebastianEwert@28 217 if parameter.loadInternalMagFreqResp
SebastianEwert@28 218 % load example included in toolbox
SebastianEwert@28 219
SebastianEwert@28 220 fullFilenameMfile = mfilename('fullpath');
SebastianEwert@28 221 [pathstr,name,ext] = fileparts(fullFilenameMfile);
SebastianEwert@28 222 dirRootIRs = fullfile(pathstr,'../degradationData');
SebastianEwert@28 223
SebastianEwert@28 224 names_internal = {'Beatles_NorwegianWood','Beethoven_Appasionata_Rwc'};
SebastianEwert@28 225 indexInternal = find(strcmpi(names_internal,parameter.internalMagFreqResp), 1);
SebastianEwert@28 226 if isempty(indexInternal)
SebastianEwert@28 227 error('Please specify a valid internal name')
SebastianEwert@28 228 end
SebastianEwert@28 229
SebastianEwert@28 230 switch indexInternal
SebastianEwert@28 231 case 1
SebastianEwert@28 232 file = fullfile(dirRootIRs,'SpecEnvelopes/Beatles_NorwegianWood.mat');
SebastianEwert@28 233 case 2
SebastianEwert@28 234 file = fullfile(dirRootIRs,'SpecEnvelopes/Beethoven_Appasionata_Rwc.mat');
SebastianEwert@28 235 end
SebastianEwert@28 236 load(file, 'destMagFreqResp', 'destMagFreqResp_freqs');
SebastianEwert@28 237
SebastianEwert@28 238 elseif parameter.loadNoiseColorPreset
SebastianEwert@28 239 switch(lower( parameter.noiseColorPreset))
SebastianEwert@28 240 case 'white'
SebastianEwert@28 241 freqExponent = 0;
SebastianEwert@28 242 case 'pink'
SebastianEwert@28 243 freqExponent = 0.5;
SebastianEwert@28 244 case 'brown'
SebastianEwert@28 245 freqExponent = 1;
SebastianEwert@28 246 case 'blue'
SebastianEwert@28 247 freqExponent = -0.5;
SebastianEwert@28 248 case 'violet'
SebastianEwert@28 249 freqExponent = -1;
SebastianEwert@28 250 end
SebastianEwert@28 251
SebastianEwert@28 252 lengthMagResp = parameter.fftLength/2+1;
SebastianEwert@28 253 destMagFreqResp_freqs = linspace(0,samplingFreq/2,lengthMagResp);
SebastianEwert@28 254 magResp = 1./destMagFreqResp_freqs.^freqExponent;
SebastianEwert@28 255 magResp(1) = 1;
SebastianEwert@28 256 destMagFreqResp = 20 * log10(magResp);
SebastianEwert@28 257
SebastianEwert@28 258 elseif parameter.computeMagFreqRespFromAudio
SebastianEwert@28 259 % compute destMagFreqResp as mean spectral vector from given audio data
SebastianEwert@28 260 [destMagFreqResp,destMagFreqResp_freqs] = internal_computeMeanSpectralVector(...
SebastianEwert@28 261 parameter.computeMagFreqRespFromAudio_audioData,parameter.computeMagFreqRespFromAudio_sf,parameter.fftLength);
SebastianEwert@28 262 else
SebastianEwert@28 263 destMagFreqResp = parameter.destMagFreqResp;
SebastianEwert@28 264 destMagFreqResp_freqs = parameter.destMagFreqResp_freqs;
SebastianEwert@28 265 end
SebastianEwert@28 266
SebastianEwert@28 267 % standardize destMagFreqResp
SebastianEwert@28 268 destMagFreqResp = internal_standardizeMagFreqResp(destMagFreqResp);
SebastianEwert@28 269 end
SebastianEwert@28 270
SebastianEwert@28 271
SebastianEwert@28 272
SebastianEwert@28 273
SebastianEwert@28 274
SebastianEwert@28 275