Mercurial > hg > webaudioevaluationtool
comparison js/loudness.js @ 2224:760719986df3
Tidy up file locations.
author | Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk> |
---|---|
date | Thu, 14 Apr 2016 13:54:24 +0100 |
parents | |
children | aca96a5183be |
comparison
equal
deleted
inserted
replaced
2222:4d1aa94202e3 | 2224:760719986df3 |
---|---|
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 if (typeof OfflineAudioContext == "undefined"){ | |
12 var OfflineAudioContext = webkitOfflineAudioContext; | |
13 } | |
14 | |
15 function calculateLoudness(buffer, timescale, target, offlineContext) | |
16 { | |
17 // This function returns the EBU R 128 specification loudness model and sets the linear gain required to match -23 LUFS | |
18 // buffer -> Web Audio API Buffer object | |
19 // timescale -> M or Momentary (returns Array), S or Short (returns Array), | |
20 // I or Integrated (default, returns number) | |
21 // target -> default is -23 LUFS but can be any LUFS measurement. | |
22 | |
23 if (buffer == undefined) | |
24 { | |
25 return 0; | |
26 } | |
27 if (timescale == undefined) | |
28 { | |
29 timescale = "I"; | |
30 } | |
31 if (target == undefined) | |
32 { | |
33 target = -23; | |
34 } | |
35 if (offlineContext == undefined) | |
36 { | |
37 offlineContext = new OfflineAudioContext(audioContext.destination.channelCount, buffer.buffer.duration*audioContext.sampleRate, audioContext.sampleRate); | |
38 } | |
39 // Create the required filters | |
40 var KFilter = offlineContext.createBiquadFilter(); | |
41 KFilter.type = "highshelf"; | |
42 KFilter.gain.value = 4; | |
43 KFilter.frequency.value = 1500; | |
44 | |
45 var HPFilter = offlineContext.createBiquadFilter(); | |
46 HPFilter.type = "highpass"; | |
47 HPFilter.Q.value = 0.5; | |
48 HPFilter.frequency.value = 38; | |
49 // copy Data into the process buffer | |
50 var processSource = offlineContext.createBufferSource(); | |
51 processSource.buffer = buffer.buffer; | |
52 | |
53 processSource.connect(KFilter); | |
54 KFilter.connect(HPFilter); | |
55 HPFilter.connect(offlineContext.destination); | |
56 offlineContext.oncomplete = function(renderedBuffer) { | |
57 // Have the renderedBuffer information, now continue processing | |
58 if (typeof renderedBuffer.renderedBuffer == 'object') { | |
59 renderedBuffer = renderedBuffer.renderedBuffer; | |
60 } | |
61 switch(timescale) | |
62 { | |
63 case "I": | |
64 // Calculate the Mean Squared of a signal | |
65 var MS = calculateMeanSquared(renderedBuffer,0.4,0.75); | |
66 // Calculate the Loudness of each block | |
67 var MSL = calculateLoudnessFromBlocks(MS); | |
68 // Get blocks from Absolute Gate | |
69 var LK = loudnessGate(MSL,MS,-70); | |
70 // Calculate Loudness | |
71 var LK_gate = loudnessOfBlocks(LK); | |
72 // Get blocks from Relative Gate | |
73 var RK = loudnessGate(MSL,MS,LK_gate-10); | |
74 var RK_gate = loudnessOfBlocks(RK); | |
75 buffer.buffer.lufs = RK_gate; | |
76 } | |
77 buffer.ready(); | |
78 }; | |
79 processSource.start(0); | |
80 offlineContext.startRendering(); | |
81 } | |
82 | |
83 function calculateMeanSquared(buffer,frame_dur,frame_overlap) | |
84 { | |
85 frame_size = Math.floor(buffer.sampleRate*frame_dur); | |
86 step_size = Math.floor(frame_size*(1.0-frame_overlap)); | |
87 num_frames = Math.floor((buffer.length-frame_size)/step_size); | |
88 | |
89 MS = Array(buffer.numberOfChannels); | |
90 for (var c=0; c<buffer.numberOfChannels; c++) | |
91 { | |
92 MS[c] = new Float32Array(num_frames); | |
93 var data = buffer.getChannelData(c); | |
94 for (var no=0; no<num_frames; no++) | |
95 { | |
96 MS[c][no] = 0.0; | |
97 for (var ptr=0; ptr<frame_size; ptr++) | |
98 { | |
99 var sample = data[no*step_size+ptr]; | |
100 MS[c][no] += sample*sample; | |
101 } | |
102 MS[c][no] /= frame_size; | |
103 } | |
104 } | |
105 return MS; | |
106 } | |
107 | |
108 function calculateLoudnessFromBlocks(blocks) | |
109 { | |
110 var num_frames = blocks[0].length; | |
111 var num_channels = blocks.length; | |
112 var MSL = Array(num_frames); | |
113 for (var n=0; n<num_frames; n++) | |
114 { | |
115 var sum = 0; | |
116 for (var c=0; c<num_channels; c++) | |
117 { | |
118 var G = 1.0; | |
119 if(G >= 3){G = 1.41;} | |
120 sum += blocks[c][n]*G; | |
121 } | |
122 MSL[n] = -0.691 + 10*Math.log10(sum); | |
123 } | |
124 return MSL; | |
125 } | |
126 | |
127 function loudnessGate(blocks,source,threshold) | |
128 { | |
129 var num_frames = source[0].length; | |
130 var num_channels = source.length; | |
131 var LK = Array(num_channels); | |
132 for (var c=0; c<num_channels; c++) | |
133 { | |
134 LK[c] = []; | |
135 } | |
136 | |
137 for (var n=0; n<num_frames; n++) | |
138 { | |
139 if (blocks[n] > threshold) | |
140 { | |
141 for (var c=0; c<num_channels; c++) | |
142 { | |
143 LK[c].push(source[c][n]); | |
144 } | |
145 } | |
146 } | |
147 return LK; | |
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 } |