b@4
|
1 function result = getloudness( vector, Fs, timescale, plotBOOL )
|
b@2
|
2 %GETLOUDNESS returns loudness levels according to EBU R 128 specification
|
b@2
|
3 % Function getLoudness accepts an audio file vector (vector), a sampling
|
b@2
|
4 % rate (Fs), a timescale ('M' - Momentary, 'S' - Short, 'I' - Integrated)
|
b@2
|
5 % and a boolean of whether or not to plot the results, and returns either a
|
b@2
|
6 % scalar for Integrated timescales, or a vector of loudnesses with a
|
b@2
|
7 % refresh rate of 10 Hz.
|
b@2
|
8 %
|
b@2
|
9 % Calling sequence:
|
b@2
|
10 % result = getLoudness(vector, Fs, timescale, plotBOOL)
|
b@2
|
11
|
b@2
|
12 % % Defining filter coefficients for the pre-filter, according to ITU-R BS.1770-1
|
b@2
|
13 % % Modelling a spherical head
|
b@2
|
14 % prefilterBcoeffs = [1.53512485958697, -2.69169618940638, 1.19839281085285];
|
b@2
|
15 % prefilterAcoeffs = [1.0, -1.69065929318241, 0.73248077421585];
|
b@2
|
16 %
|
b@2
|
17 % % Defining filter coefficients for the RLB, according to ITU-R BS.1770-1
|
b@2
|
18 % rlbBcoeffs = [1.0, -2.0, 1.0];
|
b@2
|
19 % rlbAcoeffs = [1.0, -1.99004745483398, 0.99007225036621];
|
b@2
|
20
|
b@2
|
21 % MY 44.1kHZ Coefficients
|
b@2
|
22
|
b@2
|
23 % prefilterBcoeffs = [1.53093728623786, -2.65120001232265, 1.16732946528280];
|
b@2
|
24 % prefilterAcoeffs = [1.0, -1.66410930225081, 0.711176041448821];
|
b@2
|
25
|
b@2
|
26 % by Pedro Pestana; modified by Brecht De Man
|
b@2
|
27
|
b@2
|
28 rlbBcoeffs = [[0.994975383507587;], [-1.98995076701517;], [0.994975383507587;]];
|
b@2
|
29 rlbAcoeffs = [1, -1.98992552008493, 0.989976013945421];
|
b@2
|
30
|
b@2
|
31 % loudness prefilter function below this one (see Pestana 2013)
|
b@4
|
32 [prefilterBcoeffs, prefilterAcoeffs] = getloudnessprefilter(10, Fs);
|
b@2
|
33
|
b@2
|
34
|
b@2
|
35 % Weighting coefficients with channel format [L, R, C, Ls, Rs]
|
b@2
|
36 nrChannels = size(vector,2);
|
b@2
|
37 switch nrChannels
|
b@2
|
38 case 1,
|
b@2
|
39 G = [1.0];
|
b@2
|
40 case 2,
|
b@2
|
41 G = [1.0 1.0];
|
b@2
|
42 case 5,
|
b@2
|
43 G = [1.0 1.0 1.0 1.41 1.41];
|
b@2
|
44 case 6,
|
b@2
|
45 G = [1.0 1.0 1.0 1.41 1.41]; % double check... AES is L,R,C,LFE,Ls,Rs
|
b@2
|
46 otherwise,
|
b@2
|
47 fprintf('Unsuported number of channels\n');
|
b@2
|
48 end
|
b@2
|
49
|
b@2
|
50 pfVector = filter(prefilterBcoeffs, prefilterAcoeffs, vector); %Apply pre-filter
|
b@2
|
51 rlbVector = filter(rlbBcoeffs, rlbAcoeffs, pfVector);% Apply RLB filter
|
b@2
|
52 vectorSquared = rlbVector.^2;
|
b@2
|
53
|
b@2
|
54 switch timescale
|
b@2
|
55 case 'M'
|
b@2
|
56 % MODE: MOMENTARY
|
b@2
|
57 winDur = 400; % window time in miliseconds
|
b@2
|
58 winSize = Fs*winDur/1000; % window duration in samples
|
b@2
|
59 olapSize = Fs*100/1000; % not specified
|
b@2
|
60 maxI = floor( length(vector)/olapSize - winSize/olapSize + 1); % number of iterations
|
b@2
|
61 for i = 1:maxI
|
b@2
|
62 blockEnergy(i,:) = sum( vectorSquared( (i-1)*olapSize+1 : (i-1)*olapSize+winSize ,:) )./winSize;
|
b@2
|
63 time(i) = (i*olapSize)/Fs;
|
b@2
|
64 end
|
b@2
|
65 gTimesZ = bsxfun(@times, blockEnergy, G);
|
b@2
|
66 loudness = -0.691 + 10 .* log10(sum(gTimesZ,2));
|
b@2
|
67
|
b@2
|
68 if(plotBOOL)
|
b@2
|
69 plot(time, loudness, 'b-', 'LineWidth', 2);
|
b@2
|
70 axis([0 length(vector)/Fs -70 0]);
|
b@2
|
71 xlabel('Time (s)');
|
b@2
|
72 ylabel('Momentary Loudness (dB)')
|
b@2
|
73 grid on;
|
b@2
|
74 end
|
b@2
|
75
|
b@2
|
76 result = loudness;
|
b@2
|
77
|
b@2
|
78 case 'S'
|
b@2
|
79 % MODE: SHORT
|
b@2
|
80 winDur = 3000; % window time in miliseconds
|
b@2
|
81 winSize = Fs*winDur/1000; % window duration in samples
|
b@2
|
82 olapSize = Fs*100/1000; % 10 Hz refresh rate as per spec
|
b@2
|
83 maxI = floor( length(vector)/olapSize - winSize/olapSize + 1); % number of iterations
|
b@2
|
84 for i = 1:maxI
|
b@2
|
85 blockEnergy(i,:) = sum( vectorSquared( (i-1)*olapSize+1 : (i-1)*olapSize+winSize ,:) )./winSize;
|
b@2
|
86 time(i) = (i*olapSize)/Fs;
|
b@2
|
87 end
|
b@2
|
88 gTimesZ = bsxfun(@times, blockEnergy, G);
|
b@2
|
89 loudness = -0.691 + 10 .* log10(sum(gTimesZ,2));
|
b@2
|
90
|
b@2
|
91 if(plotBOOL)
|
b@2
|
92 plot(time, loudness, 'r-.', 'LineWidth', 2);
|
b@2
|
93 axis([0 length(vector)/Fs -70 0]);
|
b@2
|
94 xlabel('Time (s)');
|
b@2
|
95 ylabel('Short Term Loudness (dB)')
|
b@2
|
96 grid on;
|
b@2
|
97 end
|
b@2
|
98
|
b@2
|
99 result = loudness;
|
b@2
|
100
|
b@2
|
101 case 'I'
|
b@2
|
102
|
b@2
|
103 % MODE: INTEGRATED
|
b@2
|
104
|
b@2
|
105 % Gating block interval duration
|
b@2
|
106 winDur = 400; % in miliseconds
|
b@2
|
107 winSize = Fs*winDur/1000; % in samples
|
b@2
|
108 overlap = 0.5; % overlap. Specs: 1/(1-n) must be a natural number exc. 1
|
b@2
|
109 olapSize = overlap*winSize; % block start interval
|
b@2
|
110 maxI = floor( length(vector)/olapSize - winSize/olapSize + 1 ); % number of iterations
|
b@2
|
111
|
b@2
|
112 for i = 1:maxI
|
b@2
|
113 % equation 3
|
b@2
|
114 blockEnergy(i,:) = sum(vectorSquared( (i-1)*olapSize+1 : (i-1)*olapSize+winSize,:))./winSize;
|
b@2
|
115 end
|
b@2
|
116
|
b@2
|
117 % equation 4 - loudness
|
b@2
|
118 gTimesZ = bsxfun(@times, blockEnergy, G); %element-by-elemnt array multiple of G and blockEnergy
|
b@2
|
119 loudness = -0.691 + 10 .* log10(sum(gTimesZ,2));
|
b@2
|
120
|
b@2
|
121 % applying the absolute gate
|
b@2
|
122 absGate = loudness >= -70;
|
b@2
|
123 absgatedLoudness = loudness(absGate);
|
b@2
|
124 absgatedEnergy = blockEnergy(absGate,:);
|
b@2
|
125 overallAbsLoudness = -0.691 + 10 .* log10( sum((sum(absgatedEnergy)./size(absgatedEnergy,1)).*G) );
|
b@2
|
126
|
b@2
|
127 % applying the relative gate 8 dB down from overallAbsLoudness
|
b@2
|
128 relGateLevel = overallAbsLoudness - 8;
|
b@2
|
129 relGate = loudness >= relGateLevel;
|
b@2
|
130 relgatedLoudness = loudness(relGate);
|
b@2
|
131 relgateEnergy = blockEnergy(relGate,:);
|
b@2
|
132 overallRelLoudness = -0.691 + 10 .* log10( sum((sum(relgateEnergy)./size(relgateEnergy,1)).*G) );
|
b@2
|
133
|
b@2
|
134 result = overallRelLoudness;
|
b@2
|
135
|
b@2
|
136
|
b@2
|
137 case 'ITU'
|
b@2
|
138
|
b@2
|
139 % MODE: ITU (brecht)
|
b@2
|
140
|
b@2
|
141 % Gating block interval duration
|
b@2
|
142 winDur = 400; % in miliseconds
|
b@2
|
143 winSize = Fs*winDur/1000; % in samples
|
b@2
|
144 overlap = 0.75; % overlap. Specs: 1/(1-n) must be a natural number exc. 1
|
b@2
|
145 olapSize = overlap*winSize; % block start interval
|
b@2
|
146 maxI = floor( length(vector)/olapSize - winSize/olapSize + 1 ); % number of iterations
|
b@2
|
147
|
b@2
|
148 for i = 1:maxI
|
b@2
|
149 % equation 3
|
b@2
|
150 blockEnergy(i,:) = sum(vectorSquared( (i-1)*olapSize+1 : (i-1)*olapSize+winSize,:))./winSize;
|
b@2
|
151 end
|
b@2
|
152
|
b@2
|
153 % equation 4 - loudness
|
b@2
|
154 gTimesZ = bsxfun(@times, blockEnergy, G); %element-by-elemnt array multiple of G and blockEnergy
|
b@2
|
155 loudness = -0.691 + 10 .* log10(sum(gTimesZ,2));
|
b@2
|
156
|
b@2
|
157 % applying the absolute gate
|
b@2
|
158 absGate = loudness >= -70;
|
b@2
|
159 absgatedLoudness = loudness(absGate);
|
b@2
|
160 absgatedEnergy = blockEnergy(absGate,:);
|
b@2
|
161 overallAbsLoudness = -0.691 + 10 .* log10( sum((sum(absgatedEnergy)./size(absgatedEnergy,1)).*G) );
|
b@2
|
162
|
b@2
|
163 % applying the relative gate 10 dB down from overallAbsLoudness
|
b@2
|
164 relGateLevel = overallAbsLoudness - 10;
|
b@2
|
165 relGate = loudness >= relGateLevel;
|
b@2
|
166 relgatedLoudness = loudness(relGate);
|
b@2
|
167 relgateEnergy = blockEnergy(relGate,:);
|
b@2
|
168 overallRelLoudness = -0.691 + 10 .* log10( sum((sum(relgateEnergy)./size(relgateEnergy,1)).*G) );
|
b@2
|
169
|
b@2
|
170 result = overallRelLoudness;
|
b@2
|
171
|
b@2
|
172 case 'B'
|
b@2
|
173
|
b@2
|
174 % MODE: INTEGRATED - modified by Brecht De Man to follow ITU-R BS.1770-3
|
b@2
|
175 % and Pestana 2013
|
b@2
|
176
|
b@2
|
177 % GAIN???
|
b@2
|
178
|
b@2
|
179 % Gating block interval duration
|
b@2
|
180 winDur = 280; % in miliseconds (see Pestana 2013)
|
b@2
|
181 winSize = Fs*winDur/1000; % in samples
|
b@2
|
182 overlap = 0.75; %! % overlap. Specs: 1/(1-n) must be a natural number exc. 1
|
b@2
|
183 olapSize = overlap*winSize; % block start interval
|
b@2
|
184 maxI = floor( length(vector)/olapSize - winSize/olapSize + 1 ); % number of iterations
|
b@2
|
185
|
b@2
|
186 for i = 1:maxI
|
b@2
|
187 % equation 3
|
b@2
|
188 blockEnergy(i,:) = sum(vectorSquared( (i-1)*olapSize+1 : (i-1)*olapSize+winSize,:))./winSize;
|
b@2
|
189 end
|
b@2
|
190
|
b@2
|
191 % equation 4 - loudness
|
b@2
|
192 gTimesZ = bsxfun(@times, blockEnergy, G); %element-by-elemnt array multiple of G and blockEnergy
|
b@2
|
193 loudness = -0.691 + 10 .* log10(sum(gTimesZ,2));
|
b@2
|
194
|
b@2
|
195 % applying the absolute gate
|
b@2
|
196 absGate = loudness >= -70;
|
b@2
|
197 absgatedLoudness = loudness(absGate);
|
b@2
|
198 absgatedEnergy = blockEnergy(absGate,:);
|
b@2
|
199 overallAbsLoudness = -0.691 + 10 .* log10( sum((sum(absgatedEnergy)./size(absgatedEnergy,1)).*G) );
|
b@2
|
200
|
b@2
|
201 % applying the relative gate 10 dB (!) down from overallAbsLoudness
|
b@2
|
202 relGateLevel = overallAbsLoudness - 10; % !
|
b@2
|
203 relGate = loudness >= relGateLevel;
|
b@2
|
204 relgatedLoudness = loudness(relGate);
|
b@2
|
205 relgateEnergy = blockEnergy(relGate,:);
|
b@2
|
206 overallRelLoudness = -0.691 + 10 .* log10( sum((sum(relgateEnergy)./size(relgateEnergy,1)).*G) );
|
b@2
|
207
|
b@2
|
208 result = overallRelLoudness;
|
b@2
|
209
|
b@2
|
210 otherwise
|
b@2
|
211
|
b@2
|
212 % MODE: My short-time measure
|
b@2
|
213
|
b@2
|
214 winDur = 3000; % window time in miliseconds
|
b@2
|
215 winSize = Fs*winDur/1000; % window duration in samples
|
b@2
|
216 blockEnergy = sum(vectorSquared(:))./winSize;
|
b@2
|
217
|
b@2
|
218 % equation 4 - loudness
|
b@2
|
219 gTimesZ = bsxfun(@times, blockEnergy, G); %element-by-elemnt array multiple of G and blockEnergy
|
b@2
|
220 loudness = -0.691 + 10 .* log10(sum(gTimesZ,2));
|
b@2
|
221
|
b@2
|
222 winSize=0;
|
b@2
|
223 olapSize=0;
|
b@2
|
224
|
b@2
|
225 result = loudness;
|
b@2
|
226
|
b@2
|
227 end
|
b@2
|
228 end
|
b@2
|
229
|
b@2
|
230
|
b@2
|
231
|
b@4
|
232 function [prefilterBcoeffs, prefilterAcoeffs] = getloudnessprefilter(gainIndB, fs)
|
b@2
|
233 % GET LOUDNESS calculates coefficients for prefilter with arbitrary gain as
|
b@2
|
234 % specified in EBU R 128 and the modification proposed in Pestana 2013.
|
b@2
|
235 %
|
b@2
|
236 % by Brecht De Man at Centre for Digital Music on 24 April 2014
|
b@2
|
237
|
b@2
|
238 if nargin < 2
|
b@2
|
239 fs = 44100; % standard 44.1kHz
|
b@2
|
240 if nargin < 1
|
b@2
|
241 gainIndB = 4.0; % as recommended in EBU R 128 / ITU-R BS.1770-3
|
b@2
|
242 end
|
b@2
|
243 end
|
b@2
|
244
|
b@2
|
245 f0 = 1681.9;
|
b@2
|
246 %Q = ?
|
b@2
|
247 %BW = ?
|
b@2
|
248 S = 1;
|
b@2
|
249
|
b@2
|
250 A = sqrt(10^(gainIndB/20));
|
b@2
|
251 w0 = 2*pi*f0/fs;
|
b@2
|
252 alpha = sin(w0/2) * sqrt( (A+1/A) * (1/S-1) + 2);
|
b@2
|
253
|
b@2
|
254 b0 = A*((A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha);
|
b@2
|
255 b1 = -2*A*((A-1) + (A+1)*cos(w0));
|
b@2
|
256 b2 = A*((A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha);
|
b@2
|
257 a0 = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha;
|
b@2
|
258 a1 = 2*((A-1) - (A+1)*cos(w0));
|
b@2
|
259 a2 = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha;
|
b@2
|
260
|
b@2
|
261 % b0 = (1 - cos(w0))/2;
|
b@2
|
262 % b1 = 1 - cos(w0);
|
b@2
|
263 % b2 = (1 - cos(w0))/2;
|
b@2
|
264 % a0 = 1 + alpha;
|
b@2
|
265 % a1 = -2*cos(w0);
|
b@2
|
266 % a2 = 1 - alpha;
|
b@2
|
267
|
b@2
|
268 % b0 = b0/a0;
|
b@2
|
269 % b1 = b1/a0;
|
b@2
|
270 % b2 = b2/a0;
|
b@2
|
271 % a0 = a0/a0;
|
b@2
|
272 % a1 = a1/a0;
|
b@2
|
273 % a2 = a2/a0;
|
b@2
|
274
|
b@2
|
275 prefilterBcoeffs = [b0, b1, b2];
|
b@2
|
276 prefilterAcoeffs = [a0, a1, a2];
|
b@2
|
277
|
b@2
|
278
|
b@2
|
279 % % DEBUG: comparison with ITU/EBU standard hard coded coefficients
|
b@2
|
280 % [Hpf,Wpf] = freqz(prefilterBcoeffs, prefilterAcoeffs, 20000, fs); % response in radians per sample
|
b@2
|
281 %
|
b@2
|
282 % subplot(2,1,1)
|
b@2
|
283 % semilogx(20*log10(abs(Hpf)));
|
b@2
|
284 % axis([0 20000 -10 15]);
|
b@2
|
285 % grid on
|
b@2
|
286 % set(gca, 'XTickMode','manual');
|
b@2
|
287 % set(gca, 'XTickLabel',num2str(get(gca,'XTick')'));
|
b@2
|
288 %
|
b@2
|
289 %
|
b@2
|
290 % % fs=48k, 4dB gain
|
b@2
|
291 % prefilterBcoeffs = [1.53512485958697, -2.69169618940638, 1.19839281085285];
|
b@2
|
292 % prefilterAcoeffs = [1.0, -1.69065929318241, 0.73248077421585];
|
b@2
|
293 %
|
b@2
|
294 % [Hpf,Wpf] = freqz(prefilterBcoeffs, prefilterAcoeffs, 20000,48000); % response in radians per sample
|
b@2
|
295 %
|
b@2
|
296 % subplot(2,1,2)
|
b@2
|
297 % semilogx(20*log10(abs(Hpf)));
|
b@2
|
298 % axis([0 20000 -10 10]);
|
b@2
|
299 % grid on
|
b@2
|
300 % set(gca, 'XTickMode','manual');
|
b@2
|
301 % set(gca, 'XTickLabel',num2str(get(gca,'XTick')'));
|
b@2
|
302
|
b@2
|
303
|
b@2
|
304 % see
|
b@2
|
305 % S. Mansbridge, S. Finn, and J. D. Reiss, "Implementation and Evaluation
|
b@2
|
306 % of Autonomous Multi-track Fader Control," 132nd Convention of the Audio
|
b@2
|
307 % Engineering Society, 2012.
|
b@2
|
308 % f_c = 1681.97;
|
b@2
|
309 % Omega = tan(pi*f_c/fs);
|
b@2
|
310 % V_H = 1.58; % probably gain (?+4dB)
|
b@2
|
311 % V_L = 1.26; % switch with V_B?
|
b@2
|
312 % V_B = 1;
|
b@2
|
313 % Q = 0.71;
|
b@2
|
314 %
|
b@2
|
315 % b(1) = V_L * Omega^2 + V_B*Omega/Q + V_H;
|
b@2
|
316 % b(2) = 2*(V_L*Omega^2 - V_H);
|
b@2
|
317 % b(3) = V_L*Omega^2 - V_B*Omega/Q + V_H;
|
b@2
|
318 % a(1) = Omega^2 + Omega/Q + 1;
|
b@2
|
319 % a(2) = 2*(Omega^2 - 1);
|
b@2
|
320 % a(3) = Omega^2 - Omega/Q + 1;
|
b@2
|
321 %
|
b@2
|
322 % b = b /a(1);
|
b@2
|
323 % a = a /a(1);
|
b@2
|
324
|
b@2
|
325 % f = 1682; % Hz
|
b@2
|
326 % S = 1; % steep without noticeable notches/peaks
|
b@2
|
327 % A = 10^(gain/40);
|
b@2
|
328 % w0 =2*pi*f/fs;
|
b@2
|
329 % alpha = sin(w0)/2 * sqrt( (A+ 1/A)*(1/S - 1) + 2 );
|
b@2
|
330 %
|
b@2
|
331 % % filter coefficients
|
b@2
|
332 % b(1)= A*( (A+1) + (A-1)*cos(w0) + 2*sqrt(A)*alpha );
|
b@2
|
333 % b(2) = -2*A*( (A-1) + (A+1)*cos(w0) );
|
b@2
|
334 % b(3) = A*( (A+1) + (A-1)*cos(w0) - 2*sqrt(A)*alpha );
|
b@2
|
335 % a(1) = (A+1) - (A-1)*cos(w0) + 2*sqrt(A)*alpha;
|
b@2
|
336 % a(2) = 2*( (A-1) - (A+1)*cos(w0) );
|
b@2
|
337 % a(3) = (A+1) - (A-1)*cos(w0) - 2*sqrt(A)*alpha;
|
b@2
|
338 %
|
b@2
|
339
|
b@2
|
340
|
b@2
|
341 % DEBUG
|
b@2
|
342 %fvtool(b,a,'Fs',fs);
|
b@2
|
343
|
b@2
|
344 end |