changeset 486:3bcee92d95ab Dev_main

Fixed loudness. Now passes all EBU 3341 tests for Integrated loudness. Fixed WAVE decoder error for non-mono sources.
author Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk>
date Tue, 26 Jan 2016 11:01:55 +0000
parents 92f26057b934
children ea2c4e515f44
files WAVE.js loudness.js
diffstat 2 files changed, 94 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- a/WAVE.js	Mon Jan 25 17:29:34 2016 +0000
+++ b/WAVE.js	Tue Jan 26 11:01:55 2016 +0000
@@ -108,7 +108,7 @@
             }
         } else if (this.type == 1)
         {
-            data_view = new Float32Array(this.num_samples);
+            data_view = new Float32Array(this.num_samples*this.num_channels);
             integerConvert(new Uint8Array(IOArrayBuffer.slice(sampleDataOffset,sampleDataOffset+this.data_size)),data_view,this.bits_per_sample/8);
         }
         deInterlace(data_view,this.decoded_data);
--- a/loudness.js	Mon Jan 25 17:29:34 2016 +0000
+++ b/loudness.js	Tue Jan 26 11:01:55 2016 +0000
@@ -62,121 +62,107 @@
 		switch(timescale)
 		{
 		case "I":
-			var blockEnergy = calculateProcessedLoudness(renderedBuffer, 400, 0.75);
-			// Apply the absolute gate
-			var loudness = calculateLoudnessFromChannelBlocks(blockEnergy);
-			var absgatedEnergy = new Array(blockEnergy.length);
-			for (var c=0; c<blockEnergy.length; c++)
-			{
-				absgatedEnergy[c] = [];
-			}
-			for (var i=0; i<loudness.length; i++)
-			{
-				if (loudness[i] >= -70)
-				{
-					for (var c=0; c<blockEnergy.length; c++)
-					{
-						absgatedEnergy[c].push(blockEnergy[c][i]);
-					}
-				}
-			}
-			var overallAbsLoudness = calculateOverallLoudnessFromChannelBlocks(absgatedEnergy);
-			
-			//applying the relative gate 8 dB down from overallAbsLoudness
-			var relGateLevel = overallAbsLoudness - 8;
-			var relgateEnergy = new Array(blockEnergy.length);
-			for (var c=0; c<blockEnergy.length; c++)
-			{
-				relgateEnergy[c] = [];
-			}
-			for (var i=0; i<loudness.length; i++)
-			{
-				if (loudness[i] >= relGateLevel)
-				{
-					for (var c=0; c<blockEnergy.length; c++)
-					{
-						relgateEnergy[c].push(blockEnergy[c][i]);
-					}
-				}
-			}
-			var overallRelLoudness = calculateOverallLoudnessFromChannelBlocks(relgateEnergy);
-			buffer.buffer.lufs =  overallRelLoudness;
-			buffer.ready();
+            // Calculate the Mean Squared of a signal
+            var MS = calculateMeanSquared(renderedBuffer,0.4,0.75);
+            // Calculate the Loudness of each block
+            var MSL = calculateLoudnessFromBlocks(MS);
+            // Get blocks from Absolute Gate
+            var LK = loudnessGate(MSL,MS,-70);
+            // Calculate Loudness
+            var LK_gate = loudnessOfBlocks(LK);
+            // Get blocks from Relative Gate
+            var RK = loudnessGate(MSL,MS,LK_gate-10);
+            var RK_gate = loudnessOfBlocks(RK);
+            buffer.buffer.lufs = RK_gate;
 		}
 	};
 	offlineContext.startRendering();
 }
 
-function calculateProcessedLoudness(buffer, winDur, overlap)
+function calculateMeanSquared(buffer,frame_dur,frame_overlap)
 {
-	// Buffer		Web Audio buffer node
-	// winDur		Window Duration in milliseconds
-	// overlap		Window overlap as normalised (0.5 = 50% overlap);
-	if (buffer == undefined)
-	{
-		return 0;
-	}
-	if (winDur == undefined)
-	{
-		winDur = 400;
-	}
-	if (overlap == undefined)
-	{
-		overlap = 0.5;
-	}
-	var winSize = buffer.sampleRate*winDur/1000;
-	var olapSize = (1-overlap)*winSize;
-	var numberOfFrames = Math.floor(buffer.length/olapSize - winSize/olapSize + 1);
-	var blockEnergy = new Array(buffer.numberOfChannels);
-	for (var channel = 0; channel < buffer.numberOfChannels; channel++)
-	{
-		blockEnergy[channel] = new Float32Array(numberOfFrames);
-		var data = buffer.getChannelData(channel);
-		for (var i=0; i<numberOfFrames; i++)
-		{
-			var sigma = 0;
-			for (var n=i*olapSize; n < i*olapSize+winSize; n++)
-			{
-				sigma += Math.pow(data[n],2);
-			}
-			blockEnergy[channel][i] = sigma/winSize;
-		}
-	}
-	return blockEnergy;
+    frame_size = Math.floor(buffer.sampleRate*frame_dur);
+    step_size = Math.floor(frame_size*(1.0-frame_overlap));
+    num_frames = Math.floor((buffer.length-frame_size)/step_size);
+    
+    MS = Array(buffer.numberOfChannels);
+    for (var c=0; c<buffer.numberOfChannels; c++)
+    {
+        MS[c] = new Float32Array(num_frames);
+        var data = buffer.getChannelData(c);
+        for (var no=0; no<num_frames; no++)
+        {
+            MS[c][no] = 0.0;
+            for (var ptr=0; ptr<frame_size; ptr++)
+            {
+                var sample = data[no*step_size+ptr];
+                MS[c][no] += sample*sample;
+            }
+            MS[c][no] /= frame_size;
+        }
+    }
+    return MS;
 }
-function calculateLoudnessFromChannelBlocks(blockEnergy)
+
+function calculateLoudnessFromBlocks(blocks)
 {
-	// Loudness
-	var loudness = new Float32Array(blockEnergy[0].length);
-	for (var i=0; i<blockEnergy[0].length; i++)
-	{
-		var sigma = 0;
-		for (var channel = 0; channel < blockEnergy.length; channel++)
-		{
-			var G = 1.0;
-			if (channel >= 4) {G = 1.41;}
-			sigma += blockEnergy[channel][i]*G;
-		}
-		loudness[i] = -0.691 + 10*Math.log10(sigma);
-	}
-	return loudness;
+    var num_frames = blocks[0].length;
+    var num_channels = blocks.length;
+    var MSL = Array(num_frames);
+    for (var n=0; n<num_frames; n++)
+    {
+        var sum = 0;
+        for (var c=0; c<num_channels; c++)
+        {
+            var G = 1.0;
+            if(G >= 3){G = 1.41;}
+            sum += blocks[c][n]*G;
+        }
+        MSL[n] = -0.691 + 10*Math.log10(sum);
+    }
+    return MSL;
 }
-function calculateOverallLoudnessFromChannelBlocks(blockEnergy)
+
+function loudnessGate(blocks,source,threshold)
 {
-	// Loudness
-	var summation = 0;
-	for (var channel = 0; channel < blockEnergy.length; channel++)
-	{
-		var G = 1.0;
-		if (channel >= 4) {G = 1.41;}
-		var sigma = 0;
-		for (var i=0; i<blockEnergy[0].length; i++)
-		{
-			sigma += blockEnergy[channel][i];
-		}
-        sigma /= blockEnergy[0].length;
-        sigma *= G;
-		summation+= sigma;
-	}
-	return -0.691 + 10*Math.log10(summation);;
+    var num_frames = source[0].length;
+    var num_channels = source.length;
+    var LK = Array(num_channels);
+    for (var c=0; c<num_channels; c++)
+    {
+        LK[c] = [];
+    }
+    
+    for (var n=0; n<num_frames; n++)
+    {
+        if (blocks[n] > threshold)
+        {
+            for (var c=0; c<num_channels; c++)
+            {
+                LK[c].push(source[c][n]);
+            }
+        }
+    }
+    return LK;
 }
+
+function loudnessOfBlocks(blocks)
+{
+    var num_frames = blocks[0].length;
+    var num_channels = blocks.length;
+    var loudness = 0.0;
+    for (var n=0; n<num_frames; n++)
+    {
+        var sum = 0;
+        for (var c=0; c<num_channels; c++)
+        {
+            var G = 1.0;
+            if(G >= 3){G = 1.41;}
+            sum += blocks[c][n]*G;
+        }
+        sum /= num_frames;
+        loudness += sum;
+    }
+    loudness = -0.691 + 10 * Math.log10(loudness);
+    return loudness;
+}
\ No newline at end of file