matthiasm@0
|
1 function [f_audio_out,timepositions_afterDegr] = degradationUnit_applyImpulseResponse(f_audio, samplingFreq, timepositions_beforeDegr, parameter)
|
matthiasm@0
|
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
3 % Name: degradation_applyImpulseResponse
|
matthiasm@0
|
4 % Date of Revision: 2013-04
|
matthiasm@0
|
5 % Programmer: Sebastian Ewert
|
matthiasm@0
|
6 %
|
matthiasm@0
|
7 % Description:
|
matthiasm@0
|
8 % - applies an FIR filter
|
matthiasm@0
|
9 % - takes care that f_audio and firfilter use same sampling frequency
|
matthiasm@0
|
10 % - set samplingFreqFilter = samplingFreq if you do not which to resample
|
matthiasm@0
|
11 % the filter
|
matthiasm@0
|
12 % - predefines IR: 'GreatHall1','Classroom1','Octagon1',
|
matthiasm@0
|
13 % 'GoogleNexusOneFrontSpeaker','GoogleNexusOneFrontMic'
|
matthiasm@0
|
14 % 'VinylPlayer1960'
|
matthiasm@0
|
15 %
|
matthiasm@0
|
16 % Input:
|
matthiasm@0
|
17 % f_audio - audio signal \in [-1,1]^{NxC} with C being the number of
|
matthiasm@0
|
18 % channels
|
matthiasm@0
|
19 % samplingFreq - sampling frequency of f_audio
|
matthiasm@0
|
20 % timepositions_beforeDegr - some degradations delay the input signal. If
|
matthiasm@0
|
21 % some points in time are given via this
|
matthiasm@0
|
22 % parameter, timepositions_afterDegr will
|
matthiasm@0
|
23 % return the corresponding positions in the
|
matthiasm@0
|
24 % output. Set to [] if unavailable. Set f_audio
|
matthiasm@0
|
25 % and samplingFreq to [] to compute only
|
matthiasm@0
|
26 % timepositions_afterDegr.
|
matthiasm@0
|
27 %
|
matthiasm@0
|
28 % Input (optional): parameter
|
matthiasm@0
|
29 % .presetIR = 'GreatHall1'; - load IR from a ADT preset
|
matthiasm@0
|
30 % .impulseResponse = []; - IR to apply
|
matthiasm@0
|
31 % .impulseResponseSampFreq = 0; - sampling rate of IR
|
matthiasm@0
|
32 % .normalizeOutputAudio = 1 - normalize output
|
matthiasm@0
|
33 % .normalizeImpulseResponse = 0 - l1-normalize firfilter before
|
matthiasm@0
|
34 % application
|
matthiasm@0
|
35 %
|
matthiasm@0
|
36 % Output:
|
matthiasm@0
|
37 % f_audio_out - audio signal \in [-1,1]^{NxC} with C being the number of
|
matthiasm@0
|
38 % channels
|
matthiasm@0
|
39 % timepositions_afterDegr - time positions corresponding to timepositions_beforeDegr
|
matthiasm@0
|
40 %
|
matthiasm@0
|
41 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
42
|
matthiasm@0
|
43 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
44 % Audio Degradation Toolbox
|
matthiasm@0
|
45 %
|
matthiasm@0
|
46 % Centre for Digital Music, Queen Mary University of London.
|
matthiasm@0
|
47 % This file copyright 2013 Sebastian Ewert, Matthias Mauch and QMUL.
|
matthiasm@0
|
48 %
|
matthiasm@0
|
49 % This program is free software; you can redistribute it and/or
|
matthiasm@0
|
50 % modify it under the terms of the GNU General Public License as
|
matthiasm@0
|
51 % published by the Free Software Foundation; either version 2 of the
|
matthiasm@0
|
52 % License, or (at your option) any later version. See the file
|
matthiasm@0
|
53 % COPYING included with this distribution for more information.
|
matthiasm@0
|
54 %
|
matthiasm@0
|
55 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
56
|
matthiasm@0
|
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
58 % Check parameters
|
matthiasm@0
|
59 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
60 if nargin<4
|
matthiasm@0
|
61 parameter=[];
|
matthiasm@0
|
62 end
|
matthiasm@0
|
63 if nargin<3
|
matthiasm@0
|
64 timepositions_beforeDegr=[];
|
matthiasm@0
|
65 end
|
matthiasm@0
|
66 if nargin<2
|
matthiasm@0
|
67 error('Please specify input data');
|
matthiasm@0
|
68 end
|
matthiasm@0
|
69 if isfield(parameter,'loadInternalIR')==0
|
matthiasm@0
|
70 parameter.loadInternalIR = 1;
|
matthiasm@0
|
71 end
|
matthiasm@0
|
72 if isfield(parameter,'internalIR')==0
|
matthiasm@0
|
73 parameter.internalIR = 'GreatHall1';
|
matthiasm@0
|
74 end
|
matthiasm@0
|
75 if isfield(parameter,'normalizeOutputAudio')==0
|
matthiasm@0
|
76 parameter.normalizeOutputAudio = 1;
|
matthiasm@0
|
77 end
|
matthiasm@0
|
78 if isfield(parameter,'normalizeImpulseResponse')==0
|
matthiasm@0
|
79 parameter.normalizeImpulseResponse = 0;
|
matthiasm@0
|
80 end
|
matthiasm@0
|
81 if isfield(parameter,'impulseResponse')==0
|
matthiasm@0
|
82 parameter.impulseResponse = [];
|
matthiasm@0
|
83 end
|
matthiasm@0
|
84 if isfield(parameter,'impulseResponseSampFreq')==0
|
matthiasm@0
|
85 parameter.impulseResponseSampFreq = 0;
|
matthiasm@0
|
86 end
|
matthiasm@0
|
87
|
matthiasm@0
|
88 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
89 % Main program
|
matthiasm@0
|
90 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
matthiasm@0
|
91
|
matthiasm@0
|
92 if parameter.loadInternalIR
|
matthiasm@0
|
93 % load IR included in toolbox
|
matthiasm@0
|
94
|
matthiasm@0
|
95 fullFilenameMfile = mfilename('fullpath');
|
matthiasm@0
|
96 [pathstr,name,ext] = fileparts(fullFilenameMfile);
|
matthiasm@0
|
97 dirRootIRs = fullfile(pathstr,'degradationData');
|
matthiasm@0
|
98
|
matthiasm@0
|
99 names_internalIR = {'GreatHall1','Classroom1','Octagon1','GoogleNexusOneFrontSpeaker','GoogleNexusOneFrontMic','VinylPlayer1960'};
|
matthiasm@0
|
100 indexIR = find(strcmpi(names_internalIR,parameter.internalIR), 1);
|
matthiasm@0
|
101 if isempty(indexIR)
|
matthiasm@0
|
102 error('Please specify a valid preset')
|
matthiasm@0
|
103 end
|
matthiasm@0
|
104
|
matthiasm@0
|
105 switch indexIR
|
matthiasm@0
|
106 case 1
|
matthiasm@0
|
107 file = fullfile(dirRootIRs,'RoomResponses/GreatHall_Omni_x06y06.wav');
|
matthiasm@0
|
108 case 2
|
matthiasm@0
|
109 file = fullfile(dirRootIRs,'RoomResponses/Classroom_Omni_30x20y.wav');
|
matthiasm@0
|
110 case 3
|
matthiasm@0
|
111 file = fullfile(dirRootIRs,'RoomResponses/Octagon_Omni_x06y06.wav');
|
matthiasm@0
|
112 case 4
|
matthiasm@0
|
113 file = fullfile(dirRootIRs,'PhoneResponses/IR_GoogleNexusOneFrontSpeaker.wav');
|
matthiasm@0
|
114 case 5
|
matthiasm@0
|
115 file = fullfile(dirRootIRs,'PhoneResponses/IR_GoogleNexusOneFrontMic.wav');
|
matthiasm@0
|
116 case 6
|
matthiasm@0
|
117 file = fullfile(dirRootIRs,'VinylSim/ImpulseReponseVinylPlayer1960_smoothed.wav');
|
matthiasm@0
|
118 end
|
matthiasm@0
|
119 [parameter.impulseResponse,parameter.impulseResponseSampFreq] = wavread(file);
|
matthiasm@0
|
120 end
|
matthiasm@0
|
121
|
matthiasm@0
|
122 fs = samplingFreq;
|
matthiasm@0
|
123 h = parameter.impulseResponse;
|
matthiasm@0
|
124 fs_h = parameter.impulseResponseSampFreq;
|
matthiasm@0
|
125 h_org = h;
|
matthiasm@0
|
126
|
matthiasm@0
|
127 f_audio_out = [];
|
matthiasm@0
|
128 if ~isempty(f_audio)
|
matthiasm@0
|
129
|
matthiasm@0
|
130 if (size(h,2) > 1) && (size(h,1) ~= 1)
|
matthiasm@0
|
131 error('Multichannel impulse responses are not supported');
|
matthiasm@0
|
132 end
|
matthiasm@0
|
133
|
matthiasm@0
|
134 if fs ~= fs_h
|
matthiasm@0
|
135 h = resample(h,fs,fs_h);
|
matthiasm@0
|
136 end
|
matthiasm@0
|
137
|
matthiasm@0
|
138 if parameter.normalizeImpulseResponse
|
matthiasm@0
|
139 h = h / max(abs(h));
|
matthiasm@0
|
140 end
|
matthiasm@0
|
141
|
matthiasm@0
|
142 f_audio_out = fftfilt(h,f_audio);
|
matthiasm@0
|
143
|
matthiasm@0
|
144 if parameter.normalizeOutputAudio
|
matthiasm@0
|
145 f_audio_out = adthelper_normalizeAudio(f_audio_out, samplingFreq);
|
matthiasm@0
|
146 end
|
matthiasm@0
|
147 end
|
matthiasm@0
|
148
|
matthiasm@0
|
149 % This degradation does impose a delay
|
matthiasm@0
|
150 timepositions_afterDegr = [];
|
matthiasm@0
|
151 if ~isempty(timepositions_beforeDegr)
|
matthiasm@0
|
152 % approximation via group delay
|
matthiasm@0
|
153
|
matthiasm@0
|
154 [Gd,W] = mygrpdelay(h_org,1,fs_h,2048);
|
matthiasm@0
|
155 %[Gd,W] = grpdelay(h_org,1,1024); % Matlab's own group delay function. Failt for some filters considerably.
|
matthiasm@0
|
156
|
matthiasm@0
|
157 %figure;
|
matthiasm@0
|
158 %plot(W,Gd/samplingFreqFilter)
|
matthiasm@0
|
159
|
matthiasm@0
|
160 averageOfGroupDelays = mean(Gd);
|
matthiasm@0
|
161 timeOffset_sec = averageOfGroupDelays / fs_h;
|
matthiasm@0
|
162
|
matthiasm@0
|
163 timepositions_afterDegr = timepositions_beforeDegr + timeOffset_sec;
|
matthiasm@0
|
164 end
|
matthiasm@0
|
165
|
matthiasm@0
|
166 end
|
matthiasm@0
|
167
|
matthiasm@0
|
168 function [gd,w] = mygrpdelay(b,a,Fs,nfft)
|
matthiasm@0
|
169 % see also https://ccrma.stanford.edu/~jos/fp/Group_Delay_Computation_grpdelay_m.html
|
matthiasm@0
|
170
|
matthiasm@0
|
171 b = b(:);
|
matthiasm@0
|
172 a = a(:);
|
matthiasm@0
|
173
|
matthiasm@0
|
174 w=Fs*[0:nfft-1]/nfft;
|
matthiasm@0
|
175 oa = length(a)-1; % order of a(z)
|
matthiasm@0
|
176 oc = oa + length(b)-1; % order of c(z)
|
matthiasm@0
|
177 c = conv(b,fliplr(a)); % c(z) = b(z)*a(1/z)*z^(-oa)
|
matthiasm@0
|
178 cr = c.*[0:oc]'; % derivative of c wrt 1/z
|
matthiasm@0
|
179 num = fft(cr,nfft);
|
matthiasm@0
|
180 den = fft(c,nfft);
|
matthiasm@0
|
181 minmag = 10*eps;
|
matthiasm@0
|
182 polebins = find(abs(den)<minmag);
|
matthiasm@0
|
183 num(polebins) = 0;
|
matthiasm@0
|
184 den(polebins) = 1;
|
matthiasm@0
|
185 gd = real(num ./ den) - oa;
|
matthiasm@0
|
186
|
matthiasm@0
|
187 ns = nfft/2; % Matlab convention - should be nfft/2 + 1
|
matthiasm@0
|
188 gd = gd(1:ns);
|
matthiasm@0
|
189 w = w(1:ns);
|
matthiasm@0
|
190
|
matthiasm@0
|
191 w = w(:); % Matlab returns column vectors
|
matthiasm@0
|
192 gd = gd(:);
|
matthiasm@0
|
193
|
matthiasm@0
|
194 end
|
matthiasm@0
|
195
|
matthiasm@0
|
196
|
matthiasm@0
|
197
|