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
|