Mercurial > hg > webaudioevaluationtool
comparison loudness.js @ 517:7e85ebcab737
Merge - pre-release of 1.2.0
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Wed, 17 Feb 2016 11:16:08 +0000 |
parents | 305742b40ddc |
children | d3c573f936a9 |
comparison
equal
deleted
inserted
replaced
450:60b535a6b2a2 | 517:7e85ebcab737 |
---|---|
32 { | 32 { |
33 target = -23; | 33 target = -23; |
34 } | 34 } |
35 if (offlineContext == undefined) | 35 if (offlineContext == undefined) |
36 { | 36 { |
37 offlineContext = new OfflineAudioContext(buffer.numberOfChannels, buffer.length, buffer.sampleRate); | 37 offlineContext = new OfflineAudioContext(audioContext.destination.channelCount, buffer.buffer.duration*audioContext.sampleRate, audioContext.sampleRate); |
38 } | 38 } |
39 // Create the required filters | 39 // Create the required filters |
40 var KFilter = offlineContext.createBiquadFilter(); | 40 var KFilter = offlineContext.createBiquadFilter(); |
41 KFilter.type = "highshelf"; | 41 KFilter.type = "highshelf"; |
42 KFilter.gain.value = 4; | 42 KFilter.gain.value = 4; |
43 KFilter.frequency.value = 1480; | 43 KFilter.frequency.value = 1500; |
44 | 44 |
45 var HPFilter = offlineContext.createBiquadFilter(); | 45 var HPFilter = offlineContext.createBiquadFilter(); |
46 HPFilter.type = "highpass"; | 46 HPFilter.type = "highpass"; |
47 HPFilter.Q.value = 0.707; | 47 HPFilter.Q.value = 0.5; |
48 HPFilter.frequency.value = 60; | 48 HPFilter.frequency.value = 38; |
49 // copy Data into the process buffer | 49 // copy Data into the process buffer |
50 var processSource = offlineContext.createBufferSource(); | 50 var processSource = offlineContext.createBufferSource(); |
51 processSource.buffer = buffer; | 51 processSource.buffer = buffer.buffer; |
52 | 52 |
53 processSource.connect(KFilter); | 53 processSource.connect(KFilter); |
54 KFilter.connect(HPFilter); | 54 KFilter.connect(HPFilter); |
55 HPFilter.connect(offlineContext.destination); | 55 HPFilter.connect(offlineContext.destination); |
56 processSource.start(); | 56 processSource.start(); |
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.lufs = overallRelLoudness; | |
104 } | 77 } |
78 buffer.ready(); | |
105 }; | 79 }; |
106 offlineContext.startRendering(); | 80 offlineContext.startRendering(); |
107 } | 81 } |
108 | 82 |
109 function calculateProcessedLoudness(buffer, winDur, overlap) | 83 function calculateMeanSquared(buffer,frame_dur,frame_overlap) |
110 { | 84 { |
111 // Buffer Web Audio buffer node | 85 frame_size = Math.floor(buffer.sampleRate*frame_dur); |
112 // winDur Window Duration in milliseconds | 86 step_size = Math.floor(frame_size*(1.0-frame_overlap)); |
113 // overlap Window overlap as normalised (0.5 = 50% overlap); | 87 num_frames = Math.floor((buffer.length-frame_size)/step_size); |
114 if (buffer == undefined) | 88 |
115 { | 89 MS = Array(buffer.numberOfChannels); |
116 return 0; | 90 for (var c=0; c<buffer.numberOfChannels; c++) |
117 } | 91 { |
118 if (winDur == undefined) | 92 MS[c] = new Float32Array(num_frames); |
119 { | 93 var data = buffer.getChannelData(c); |
120 winDur = 400; | 94 for (var no=0; no<num_frames; no++) |
121 } | 95 { |
122 if (overlap == undefined) | 96 MS[c][no] = 0.0; |
123 { | 97 for (var ptr=0; ptr<frame_size; ptr++) |
124 overlap = 0.5; | 98 { |
125 } | 99 var sample = data[no*step_size+ptr]; |
126 var winSize = buffer.sampleRate*winDur/1000; | 100 MS[c][no] += sample*sample; |
127 var olapSize = (1-overlap)*winSize; | 101 } |
128 var numberOfFrames = Math.floor(buffer.length/olapSize - winSize/olapSize + 1); | 102 MS[c][no] /= frame_size; |
129 var blockEnergy = new Array(buffer.numberOfChannels); | 103 } |
130 for (var channel = 0; channel < buffer.numberOfChannels; channel++) | 104 } |
131 { | 105 return MS; |
132 blockEnergy[channel] = new Float32Array(numberOfFrames); | |
133 var data = buffer.getChannelData(channel); | |
134 for (var i=0; i<numberOfFrames; i++) | |
135 { | |
136 var sigma = 0; | |
137 for (var n=i*olapSize; n < i*olapSize+winSize; n++) | |
138 { | |
139 sigma += Math.pow(data[n],2); | |
140 } | |
141 blockEnergy[channel][i] = sigma/winSize; | |
142 } | |
143 } | |
144 return blockEnergy; | |
145 } | 106 } |
146 function calculateLoudnessFromChannelBlocks(blockEnergy) | 107 |
108 function calculateLoudnessFromBlocks(blocks) | |
147 { | 109 { |
148 // Loudness | 110 var num_frames = blocks[0].length; |
149 var loudness = new Float32Array(blockEnergy[0].length); | 111 var num_channels = blocks.length; |
150 for (var i=0; i<blockEnergy[0].length; i++) | 112 var MSL = Array(num_frames); |
151 { | 113 for (var n=0; n<num_frames; n++) |
152 var sigma = 0; | 114 { |
153 for (var channel = 0; channel < blockEnergy.length; channel++) | 115 var sum = 0; |
154 { | 116 for (var c=0; c<num_channels; c++) |
155 var G = 1.0; | 117 { |
156 if (channel >= 4) {G = 1.41;} | 118 var G = 1.0; |
157 sigma += blockEnergy[channel][i]*G; | 119 if(G >= 3){G = 1.41;} |
158 } | 120 sum += blocks[c][n]*G; |
159 loudness[i] = -0.691 + 10*Math.log10(sigma); | 121 } |
160 } | 122 MSL[n] = -0.691 + 10*Math.log10(sum); |
161 return loudness; | 123 } |
124 return MSL; | |
162 } | 125 } |
163 function calculateOverallLoudnessFromChannelBlocks(blockEnergy) | 126 |
127 function loudnessGate(blocks,source,threshold) | |
164 { | 128 { |
165 // Loudness | 129 var num_frames = source[0].length; |
166 var summation = 0; | 130 var num_channels = source.length; |
167 for (var channel = 0; channel < blockEnergy.length; channel++) | 131 var LK = Array(num_channels); |
168 { | 132 for (var c=0; c<num_channels; c++) |
169 var G = 1.0; | 133 { |
170 if (channel >= 4) {G = 1.41;} | 134 LK[c] = []; |
171 var sigma = 0; | 135 } |
172 for (var i=0; i<blockEnergy[0].length; i++) | 136 |
173 { | 137 for (var n=0; n<num_frames; n++) |
174 blockEnergy[channel][i] *= G; | 138 { |
175 sigma += blockEnergy[channel][i]; | 139 if (blocks[n] > threshold) |
176 } | 140 { |
177 sigma /= blockEnergy.length; | 141 for (var c=0; c<num_channels; c++) |
178 summation+= sigma; | 142 { |
179 } | 143 LK[c].push(source[c][n]); |
180 return -0.691 + 10*Math.log10(summation);; | 144 } |
145 } | |
146 } | |
147 return LK; | |
181 } | 148 } |
149 | |
150 function loudnessOfBlocks(blocks) | |
151 { | |
152 var num_frames = blocks[0].length; | |
153 var num_channels = blocks.length; | |
154 var loudness = 0.0; | |
155 for (var n=0; n<num_frames; n++) | |
156 { | |
157 var sum = 0; | |
158 for (var c=0; c<num_channels; c++) | |
159 { | |
160 var G = 1.0; | |
161 if(G >= 3){G = 1.41;} | |
162 sum += blocks[c][n]*G; | |
163 } | |
164 sum /= num_frames; | |
165 loudness += sum; | |
166 } | |
167 loudness = -0.691 + 10 * Math.log10(loudness); | |
168 return loudness; | |
169 } |