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);