annotate loudness.js @ 1429:c06930d14ff7

Added loudness.js to handle loudness detection modules.
author Nicholas Jillings <nickjillings@users.noreply.github.com>
date Tue, 15 Dec 2015 16:02:17 +0000
parents
children 3590373a64a1
rev   line source
nickjillings@1429 1 /**
nickjillings@1429 2 * loundess.js
nickjillings@1429 3 * Loudness module for the Web Audio Evaluation Toolbox
nickjillings@1429 4 * Allows for automatic calculation of loudness of Web Audio API Buffer objects,
nickjillings@1429 5 * return gain values to correct for a target loudness or match loudness between
nickjillings@1429 6 * multiple objects
nickjillings@1429 7 */
nickjillings@1429 8
nickjillings@1429 9 function getLoudness(buffer, result, timescale, offlineContext)
nickjillings@1429 10 {
nickjillings@1429 11 // This function returns the EBU R 128 specification loudness model
nickjillings@1429 12 // buffer -> Web Audio API Buffer object
nickjillings@1429 13 // timescale -> M or Momentary (returns Array), S or Short (returns Array),
nickjillings@1429 14 // I or Integrated (default, returns number)
nickjillings@1429 15
nickjillings@1429 16 // Create the required filters
nickjillings@1429 17 if (buffer == undefined || result == undefined)
nickjillings@1429 18 {
nickjillings@1429 19 return 0;
nickjillings@1429 20 }
nickjillings@1429 21 if (timescale == undefined)
nickjillings@1429 22 {
nickjillings@1429 23 timescale = "I";
nickjillings@1429 24 }
nickjillings@1429 25 if (offlineContext == undefined)
nickjillings@1429 26 {
nickjillings@1429 27 offlineContext = new OfflineAudioContext(buffer.numberOfChannels, buffer.length, buffer.sampleRate);
nickjillings@1429 28 }
nickjillings@1429 29 var KFilter = offlineContext.createBiquadFilter();
nickjillings@1429 30 KFilter.type = "highshelf";
nickjillings@1429 31 KFilter.gain.value = 4;
nickjillings@1429 32 KFilter.frequency.value = 1480;
nickjillings@1429 33
nickjillings@1429 34 var HPFilter = offlineContext.createBiquadFilter();
nickjillings@1429 35 HPFilter.type = "highpass";
nickjillings@1429 36 HPFilter.Q.value = 0.707;
nickjillings@1429 37 HPFilter.frequency.value = 60;
nickjillings@1429 38 // copy Data into the process buffer
nickjillings@1429 39 var processSource = offlineContext.createBufferSource();
nickjillings@1429 40 processSource.buffer = buffer;
nickjillings@1429 41
nickjillings@1429 42 processSource.connect(KFilter);
nickjillings@1429 43 KFilter.connect(HPFilter);
nickjillings@1429 44 HPFilter.connect(offlineContext.destination);
nickjillings@1429 45 processSource.start();
nickjillings@1429 46 offlineContext.startRendering().then(function(renderedBuffer) {
nickjillings@1429 47 // Have the renderedBuffer information, now continue processing
nickjillings@1429 48 console.log(renderedBuffer);
nickjillings@1429 49 switch(timescale)
nickjillings@1429 50 {
nickjillings@1429 51 case "I":
nickjillings@1429 52 var blockEnergy = calculateProcessedLoudness(renderedBuffer, 400, 0.75);
nickjillings@1429 53 // Apply the absolute gate
nickjillings@1429 54 var loudness = calculateLoudnessFromChannelBlocks(blockEnergy);
nickjillings@1429 55 var absgatedEnergy = new Array(blockEnergy.length);
nickjillings@1429 56 for (var c=0; c<blockEnergy.length; c++)
nickjillings@1429 57 {
nickjillings@1429 58 absgatedEnergy[c] = [];
nickjillings@1429 59 }
nickjillings@1429 60 for (var i=0; i<loudness.length; i++)
nickjillings@1429 61 {
nickjillings@1429 62 if (loudness[i] >= -70)
nickjillings@1429 63 {
nickjillings@1429 64 for (var c=0; c<blockEnergy.length; c++)
nickjillings@1429 65 {
nickjillings@1429 66 absgatedEnergy[c].push(blockEnergy[c][i]);
nickjillings@1429 67 }
nickjillings@1429 68 }
nickjillings@1429 69 }
nickjillings@1429 70 var overallAbsLoudness = calculateOverallLoudnessFromChannelBlocks(absgatedEnergy);
nickjillings@1429 71
nickjillings@1429 72 //applying the relative gate 8 dB down from overallAbsLoudness
nickjillings@1429 73 var relGateLevel = overallAbsLoudness - 8;
nickjillings@1429 74 var relgateEnergy = new Array(blockEnergy.length);
nickjillings@1429 75 for (var c=0; c<blockEnergy.length; c++)
nickjillings@1429 76 {
nickjillings@1429 77 relgateEnergy[c] = [];
nickjillings@1429 78 }
nickjillings@1429 79 for (var i=0; i<loudness.length; i++)
nickjillings@1429 80 {
nickjillings@1429 81 if (loudness[i] >= relGateLevel)
nickjillings@1429 82 {
nickjillings@1429 83 for (var c=0; c<blockEnergy.length; c++)
nickjillings@1429 84 {
nickjillings@1429 85 relgateEnergy[c].push(blockEnergy[c][i]);
nickjillings@1429 86 }
nickjillings@1429 87 }
nickjillings@1429 88 }
nickjillings@1429 89 var overallRelLoudness = calculateOverallLoudnessFromChannelBlocks(relgateEnergy);
nickjillings@1429 90 result[0] = overallRelLoudness;
nickjillings@1429 91 }
nickjillings@1429 92 }).catch(function(err) {
nickjillings@1429 93 console.log(err);
nickjillings@1429 94 result[0] = 1;
nickjillings@1429 95 });
nickjillings@1429 96 }
nickjillings@1429 97
nickjillings@1429 98 function calculateProcessedLoudness(buffer, winDur, overlap)
nickjillings@1429 99 {
nickjillings@1429 100 // Buffer Web Audio buffer node
nickjillings@1429 101 // winDur Window Duration in milliseconds
nickjillings@1429 102 // overlap Window overlap as normalised (0.5 = 50% overlap);
nickjillings@1429 103 if (buffer == undefined)
nickjillings@1429 104 {
nickjillings@1429 105 return 0;
nickjillings@1429 106 }
nickjillings@1429 107 if (winDur == undefined)
nickjillings@1429 108 {
nickjillings@1429 109 winDur = 400;
nickjillings@1429 110 }
nickjillings@1429 111 if (overlap == undefined)
nickjillings@1429 112 {
nickjillings@1429 113 overlap = 0.5;
nickjillings@1429 114 }
nickjillings@1429 115 var winSize = buffer.sampleRate*winDur/1000;
nickjillings@1429 116 var olapSize = overlap*winSize;
nickjillings@1429 117 var numberOfFrames = Math.floor(buffer.length/olapSize - winSize/olapSize + 1);
nickjillings@1429 118 var blockEnergy = new Array(buffer.numberOfChannels);
nickjillings@1429 119 for (var channel = 0; channel < buffer.numberOfChannels; channel++)
nickjillings@1429 120 {
nickjillings@1429 121 blockEnergy[channel] = new Float32Array(numberOfFrames);
nickjillings@1429 122 var data = buffer.getChannelData(channel);
nickjillings@1429 123 for (var i=0; i<numberOfFrames; i++)
nickjillings@1429 124 {
nickjillings@1429 125 var sigma = 0;
nickjillings@1429 126 for (var n=i*olapSize; n < i*olapSize+winSize; n++)
nickjillings@1429 127 {
nickjillings@1429 128 sigma += Math.pow(data[n],2);
nickjillings@1429 129 }
nickjillings@1429 130 blockEnergy[channel][i] = sigma/winSize;
nickjillings@1429 131 }
nickjillings@1429 132 }
nickjillings@1429 133 return blockEnergy;
nickjillings@1429 134 }
nickjillings@1429 135 function calculateLoudnessFromChannelBlocks(blockEnergy)
nickjillings@1429 136 {
nickjillings@1429 137 // Loudness
nickjillings@1429 138 var loudness = new Float32Array(blockEnergy[0].length);
nickjillings@1429 139 for (var i=0; i<blockEnergy[0].length; i++)
nickjillings@1429 140 {
nickjillings@1429 141 var sigma = 0;
nickjillings@1429 142 for (var channel = 0; channel < blockEnergy.length; channel++)
nickjillings@1429 143 {
nickjillings@1429 144 var G = 1.0;
nickjillings@1429 145 if (channel >= 4) {G = 1.41;}
nickjillings@1429 146 sigma += blockEnergy[channel][i]*G;
nickjillings@1429 147 }
nickjillings@1429 148 loudness[i] = -0.691 + 10*Math.log10(sigma);
nickjillings@1429 149 }
nickjillings@1429 150 return loudness;
nickjillings@1429 151 }
nickjillings@1429 152 function calculateOverallLoudnessFromChannelBlocks(blockEnergy)
nickjillings@1429 153 {
nickjillings@1429 154 // Loudness
nickjillings@1429 155 var summation = 0;
nickjillings@1429 156 for (var channel = 0; channel < blockEnergy.length; channel++)
nickjillings@1429 157 {
nickjillings@1429 158 var G = 1.0;
nickjillings@1429 159 if (channel >= 4) {G = 1.41;}
nickjillings@1429 160 var sigma = 0;
nickjillings@1429 161 for (var i=0; i<blockEnergy[0].length; i++)
nickjillings@1429 162 {
nickjillings@1429 163 blockEnergy[channel][i] *= G;
nickjillings@1429 164 sigma += blockEnergy[channel][i];
nickjillings@1429 165 }
nickjillings@1429 166 sigma /= blockEnergy.length;
nickjillings@1429 167 summation+= sigma;
nickjillings@1429 168 }
nickjillings@1429 169 return -0.691 + 10*Math.log10(summation);;
nickjillings@1429 170 }