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