Mercurial > hg > webaudioevaluationtool
comparison loudness.js @ 749:07c996307cbd
Bug #1510 Fixed.
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Mon, 21 Dec 2015 11:53:05 +0000 |
parents | |
children | fab881bdeeac 43801b3d6131 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 749:07c996307cbd |
---|---|
1 /** | |
2 * loundess.js | |
3 * Loudness module for the Web Audio Evaluation Toolbox | |
4 * Allows for automatic calculation of loudness of Web Audio API Buffer objects, | |
5 * return gain values to correct for a target loudness or match loudness between | |
6 * multiple objects | |
7 */ | |
8 | |
9 var interval_cal_loudness_event = null; | |
10 | |
11 function calculateLoudness(buffer, timescale, target, offlineContext) | |
12 { | |
13 // This function returns the EBU R 128 specification loudness model and sets the linear gain required to match -23 LUFS | |
14 // buffer -> Web Audio API Buffer object | |
15 // timescale -> M or Momentary (returns Array), S or Short (returns Array), | |
16 // I or Integrated (default, returns number) | |
17 // target -> default is -23 LUFS but can be any LUFS measurement. | |
18 | |
19 if (buffer == undefined) | |
20 { | |
21 return 0; | |
22 } | |
23 if (timescale == undefined) | |
24 { | |
25 timescale = "I"; | |
26 } | |
27 if (target == undefined) | |
28 { | |
29 target = -23; | |
30 } | |
31 if (offlineContext == undefined) | |
32 { | |
33 offlineContext = new OfflineAudioContext(buffer.numberOfChannels, buffer.length, buffer.sampleRate); | |
34 } | |
35 // Create the required filters | |
36 var KFilter = offlineContext.createBiquadFilter(); | |
37 KFilter.type = "highshelf"; | |
38 KFilter.gain.value = 4; | |
39 KFilter.frequency.value = 1480; | |
40 | |
41 var HPFilter = offlineContext.createBiquadFilter(); | |
42 HPFilter.type = "highpass"; | |
43 HPFilter.Q.value = 0.707; | |
44 HPFilter.frequency.value = 60; | |
45 // copy Data into the process buffer | |
46 var processSource = offlineContext.createBufferSource(); | |
47 processSource.buffer = buffer; | |
48 | |
49 processSource.connect(KFilter); | |
50 KFilter.connect(HPFilter); | |
51 HPFilter.connect(offlineContext.destination); | |
52 processSource.start(); | |
53 offlineContext.startRendering().then(function(renderedBuffer) { | |
54 // Have the renderedBuffer information, now continue processing | |
55 switch(timescale) | |
56 { | |
57 case "I": | |
58 var blockEnergy = calculateProcessedLoudness(renderedBuffer, 400, 0.75); | |
59 // Apply the absolute gate | |
60 var loudness = calculateLoudnessFromChannelBlocks(blockEnergy); | |
61 var absgatedEnergy = new Array(blockEnergy.length); | |
62 for (var c=0; c<blockEnergy.length; c++) | |
63 { | |
64 absgatedEnergy[c] = []; | |
65 } | |
66 for (var i=0; i<loudness.length; i++) | |
67 { | |
68 if (loudness[i] >= -70) | |
69 { | |
70 for (var c=0; c<blockEnergy.length; c++) | |
71 { | |
72 absgatedEnergy[c].push(blockEnergy[c][i]); | |
73 } | |
74 } | |
75 } | |
76 var overallAbsLoudness = calculateOverallLoudnessFromChannelBlocks(absgatedEnergy); | |
77 | |
78 //applying the relative gate 8 dB down from overallAbsLoudness | |
79 var relGateLevel = overallAbsLoudness - 8; | |
80 var relgateEnergy = new Array(blockEnergy.length); | |
81 for (var c=0; c<blockEnergy.length; c++) | |
82 { | |
83 relgateEnergy[c] = []; | |
84 } | |
85 for (var i=0; i<loudness.length; i++) | |
86 { | |
87 if (loudness[i] >= relGateLevel) | |
88 { | |
89 for (var c=0; c<blockEnergy.length; c++) | |
90 { | |
91 relgateEnergy[c].push(blockEnergy[c][i]); | |
92 } | |
93 } | |
94 } | |
95 var overallRelLoudness = calculateOverallLoudnessFromChannelBlocks(relgateEnergy); | |
96 buffer.lufs = overallRelLoudness; | |
97 } | |
98 }).catch(function(err) { | |
99 console.log(err); | |
100 buffer.lufs = 1; | |
101 }); | |
102 } | |
103 | |
104 function calculateProcessedLoudness(buffer, winDur, overlap) | |
105 { | |
106 // Buffer Web Audio buffer node | |
107 // winDur Window Duration in milliseconds | |
108 // overlap Window overlap as normalised (0.5 = 50% overlap); | |
109 if (buffer == undefined) | |
110 { | |
111 return 0; | |
112 } | |
113 if (winDur == undefined) | |
114 { | |
115 winDur = 400; | |
116 } | |
117 if (overlap == undefined) | |
118 { | |
119 overlap = 0.5; | |
120 } | |
121 var winSize = buffer.sampleRate*winDur/1000; | |
122 var olapSize = (1-overlap)*winSize; | |
123 var numberOfFrames = Math.floor(buffer.length/olapSize - winSize/olapSize + 1); | |
124 var blockEnergy = new Array(buffer.numberOfChannels); | |
125 for (var channel = 0; channel < buffer.numberOfChannels; channel++) | |
126 { | |
127 blockEnergy[channel] = new Float32Array(numberOfFrames); | |
128 var data = buffer.getChannelData(channel); | |
129 for (var i=0; i<numberOfFrames; i++) | |
130 { | |
131 var sigma = 0; | |
132 for (var n=i*olapSize; n < i*olapSize+winSize; n++) | |
133 { | |
134 sigma += Math.pow(data[n],2); | |
135 } | |
136 blockEnergy[channel][i] = sigma/winSize; | |
137 } | |
138 } | |
139 return blockEnergy; | |
140 } | |
141 function calculateLoudnessFromChannelBlocks(blockEnergy) | |
142 { | |
143 // Loudness | |
144 var loudness = new Float32Array(blockEnergy[0].length); | |
145 for (var i=0; i<blockEnergy[0].length; i++) | |
146 { | |
147 var sigma = 0; | |
148 for (var channel = 0; channel < blockEnergy.length; channel++) | |
149 { | |
150 var G = 1.0; | |
151 if (channel >= 4) {G = 1.41;} | |
152 sigma += blockEnergy[channel][i]*G; | |
153 } | |
154 loudness[i] = -0.691 + 10*Math.log10(sigma); | |
155 } | |
156 return loudness; | |
157 } | |
158 function calculateOverallLoudnessFromChannelBlocks(blockEnergy) | |
159 { | |
160 // Loudness | |
161 var summation = 0; | |
162 for (var channel = 0; channel < blockEnergy.length; channel++) | |
163 { | |
164 var G = 1.0; | |
165 if (channel >= 4) {G = 1.41;} | |
166 var sigma = 0; | |
167 for (var i=0; i<blockEnergy[0].length; i++) | |
168 { | |
169 blockEnergy[channel][i] *= G; | |
170 sigma += blockEnergy[channel][i]; | |
171 } | |
172 sigma /= blockEnergy.length; | |
173 summation+= sigma; | |
174 } | |
175 return -0.691 + 10*Math.log10(summation);; | |
176 } |