annotate aux/getLoudness.m @ 4:b28ffd29e6e1

Audio file preparation for listening test
author Brecht De Man <b.deman@qmul.ac.uk>
date Wed, 19 Nov 2014 18:59:51 +0000
parents 5e72201496c8
children
rev   line source
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