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 }