Mercurial > hg > webaudioevaluationtool
comparison loudness.js @ 486:3bcee92d95ab Dev_main
Fixed loudness. Now passes all EBU 3341 tests for Integrated loudness. Fixed WAVE decoder error for non-mono sources.
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Tue, 26 Jan 2016 11:01:55 +0000 |
parents | d39c99d83891 |
children | ea2c4e515f44 |
comparison
equal
deleted
inserted
replaced
485:92f26057b934 | 486:3bcee92d95ab |
---|---|
60 renderedBuffer = renderedBuffer.renderedBuffer; | 60 renderedBuffer = renderedBuffer.renderedBuffer; |
61 } | 61 } |
62 switch(timescale) | 62 switch(timescale) |
63 { | 63 { |
64 case "I": | 64 case "I": |
65 var blockEnergy = calculateProcessedLoudness(renderedBuffer, 400, 0.75); | 65 // Calculate the Mean Squared of a signal |
66 // Apply the absolute gate | 66 var MS = calculateMeanSquared(renderedBuffer,0.4,0.75); |
67 var loudness = calculateLoudnessFromChannelBlocks(blockEnergy); | 67 // Calculate the Loudness of each block |
68 var absgatedEnergy = new Array(blockEnergy.length); | 68 var MSL = calculateLoudnessFromBlocks(MS); |
69 for (var c=0; c<blockEnergy.length; c++) | 69 // Get blocks from Absolute Gate |
70 { | 70 var LK = loudnessGate(MSL,MS,-70); |
71 absgatedEnergy[c] = []; | 71 // Calculate Loudness |
72 } | 72 var LK_gate = loudnessOfBlocks(LK); |
73 for (var i=0; i<loudness.length; i++) | 73 // Get blocks from Relative Gate |
74 { | 74 var RK = loudnessGate(MSL,MS,LK_gate-10); |
75 if (loudness[i] >= -70) | 75 var RK_gate = loudnessOfBlocks(RK); |
76 { | 76 buffer.buffer.lufs = RK_gate; |
77 for (var c=0; c<blockEnergy.length; c++) | |
78 { | |
79 absgatedEnergy[c].push(blockEnergy[c][i]); | |
80 } | |
81 } | |
82 } | |
83 var overallAbsLoudness = calculateOverallLoudnessFromChannelBlocks(absgatedEnergy); | |
84 | |
85 //applying the relative gate 8 dB down from overallAbsLoudness | |
86 var relGateLevel = overallAbsLoudness - 8; | |
87 var relgateEnergy = new Array(blockEnergy.length); | |
88 for (var c=0; c<blockEnergy.length; c++) | |
89 { | |
90 relgateEnergy[c] = []; | |
91 } | |
92 for (var i=0; i<loudness.length; i++) | |
93 { | |
94 if (loudness[i] >= relGateLevel) | |
95 { | |
96 for (var c=0; c<blockEnergy.length; c++) | |
97 { | |
98 relgateEnergy[c].push(blockEnergy[c][i]); | |
99 } | |
100 } | |
101 } | |
102 var overallRelLoudness = calculateOverallLoudnessFromChannelBlocks(relgateEnergy); | |
103 buffer.buffer.lufs = overallRelLoudness; | |
104 buffer.ready(); | |
105 } | 77 } |
106 }; | 78 }; |
107 offlineContext.startRendering(); | 79 offlineContext.startRendering(); |
108 } | 80 } |
109 | 81 |
110 function calculateProcessedLoudness(buffer, winDur, overlap) | 82 function calculateMeanSquared(buffer,frame_dur,frame_overlap) |
111 { | 83 { |
112 // Buffer Web Audio buffer node | 84 frame_size = Math.floor(buffer.sampleRate*frame_dur); |
113 // winDur Window Duration in milliseconds | 85 step_size = Math.floor(frame_size*(1.0-frame_overlap)); |
114 // overlap Window overlap as normalised (0.5 = 50% overlap); | 86 num_frames = Math.floor((buffer.length-frame_size)/step_size); |
115 if (buffer == undefined) | 87 |
116 { | 88 MS = Array(buffer.numberOfChannels); |
117 return 0; | 89 for (var c=0; c<buffer.numberOfChannels; c++) |
118 } | 90 { |
119 if (winDur == undefined) | 91 MS[c] = new Float32Array(num_frames); |
120 { | 92 var data = buffer.getChannelData(c); |
121 winDur = 400; | 93 for (var no=0; no<num_frames; no++) |
122 } | 94 { |
123 if (overlap == undefined) | 95 MS[c][no] = 0.0; |
124 { | 96 for (var ptr=0; ptr<frame_size; ptr++) |
125 overlap = 0.5; | 97 { |
126 } | 98 var sample = data[no*step_size+ptr]; |
127 var winSize = buffer.sampleRate*winDur/1000; | 99 MS[c][no] += sample*sample; |
128 var olapSize = (1-overlap)*winSize; | 100 } |
129 var numberOfFrames = Math.floor(buffer.length/olapSize - winSize/olapSize + 1); | 101 MS[c][no] /= frame_size; |
130 var blockEnergy = new Array(buffer.numberOfChannels); | 102 } |
131 for (var channel = 0; channel < buffer.numberOfChannels; channel++) | 103 } |
132 { | 104 return MS; |
133 blockEnergy[channel] = new Float32Array(numberOfFrames); | |
134 var data = buffer.getChannelData(channel); | |
135 for (var i=0; i<numberOfFrames; i++) | |
136 { | |
137 var sigma = 0; | |
138 for (var n=i*olapSize; n < i*olapSize+winSize; n++) | |
139 { | |
140 sigma += Math.pow(data[n],2); | |
141 } | |
142 blockEnergy[channel][i] = sigma/winSize; | |
143 } | |
144 } | |
145 return blockEnergy; | |
146 } | 105 } |
147 function calculateLoudnessFromChannelBlocks(blockEnergy) | 106 |
107 function calculateLoudnessFromBlocks(blocks) | |
148 { | 108 { |
149 // Loudness | 109 var num_frames = blocks[0].length; |
150 var loudness = new Float32Array(blockEnergy[0].length); | 110 var num_channels = blocks.length; |
151 for (var i=0; i<blockEnergy[0].length; i++) | 111 var MSL = Array(num_frames); |
152 { | 112 for (var n=0; n<num_frames; n++) |
153 var sigma = 0; | 113 { |
154 for (var channel = 0; channel < blockEnergy.length; channel++) | 114 var sum = 0; |
155 { | 115 for (var c=0; c<num_channels; c++) |
156 var G = 1.0; | 116 { |
157 if (channel >= 4) {G = 1.41;} | 117 var G = 1.0; |
158 sigma += blockEnergy[channel][i]*G; | 118 if(G >= 3){G = 1.41;} |
159 } | 119 sum += blocks[c][n]*G; |
160 loudness[i] = -0.691 + 10*Math.log10(sigma); | 120 } |
161 } | 121 MSL[n] = -0.691 + 10*Math.log10(sum); |
162 return loudness; | 122 } |
123 return MSL; | |
163 } | 124 } |
164 function calculateOverallLoudnessFromChannelBlocks(blockEnergy) | 125 |
126 function loudnessGate(blocks,source,threshold) | |
165 { | 127 { |
166 // Loudness | 128 var num_frames = source[0].length; |
167 var summation = 0; | 129 var num_channels = source.length; |
168 for (var channel = 0; channel < blockEnergy.length; channel++) | 130 var LK = Array(num_channels); |
169 { | 131 for (var c=0; c<num_channels; c++) |
170 var G = 1.0; | 132 { |
171 if (channel >= 4) {G = 1.41;} | 133 LK[c] = []; |
172 var sigma = 0; | 134 } |
173 for (var i=0; i<blockEnergy[0].length; i++) | 135 |
174 { | 136 for (var n=0; n<num_frames; n++) |
175 sigma += blockEnergy[channel][i]; | 137 { |
176 } | 138 if (blocks[n] > threshold) |
177 sigma /= blockEnergy[0].length; | 139 { |
178 sigma *= G; | 140 for (var c=0; c<num_channels; c++) |
179 summation+= sigma; | 141 { |
180 } | 142 LK[c].push(source[c][n]); |
181 return -0.691 + 10*Math.log10(summation);; | 143 } |
144 } | |
145 } | |
146 return LK; | |
182 } | 147 } |
148 | |
149 function loudnessOfBlocks(blocks) | |
150 { | |
151 var num_frames = blocks[0].length; | |
152 var num_channels = blocks.length; | |
153 var loudness = 0.0; | |
154 for (var n=0; n<num_frames; n++) | |
155 { | |
156 var sum = 0; | |
157 for (var c=0; c<num_channels; c++) | |
158 { | |
159 var G = 1.0; | |
160 if(G >= 3){G = 1.41;} | |
161 sum += blocks[c][n]*G; | |
162 } | |
163 sum /= num_frames; | |
164 loudness += sum; | |
165 } | |
166 loudness = -0.691 + 10 * Math.log10(loudness); | |
167 return loudness; | |
168 } |