nicholas@755: /** nicholas@755: * loundess.js nicholas@755: * Loudness module for the Web Audio Evaluation Toolbox nicholas@755: * Allows for automatic calculation of loudness of Web Audio API Buffer objects, nicholas@755: * return gain values to correct for a target loudness or match loudness between nicholas@755: * multiple objects nicholas@755: */ nicholas@755: nicholas@755: var interval_cal_loudness_event = null; nicholas@755: nicholas@755: function calculateLoudness(buffer, timescale, target, offlineContext) nicholas@755: { nicholas@755: // This function returns the EBU R 128 specification loudness model and sets the linear gain required to match -23 LUFS nicholas@755: // buffer -> Web Audio API Buffer object nicholas@755: // timescale -> M or Momentary (returns Array), S or Short (returns Array), nicholas@755: // I or Integrated (default, returns number) nicholas@755: // target -> default is -23 LUFS but can be any LUFS measurement. nicholas@755: nicholas@755: if (buffer == undefined) nicholas@755: { nicholas@755: return 0; nicholas@755: } nicholas@755: if (timescale == undefined) nicholas@755: { nicholas@755: timescale = "I"; nicholas@755: } nicholas@755: if (target == undefined) nicholas@755: { nicholas@755: target = -23; nicholas@755: } nicholas@755: if (offlineContext == undefined) nicholas@755: { nicholas@755: offlineContext = new OfflineAudioContext(buffer.numberOfChannels, buffer.length, buffer.sampleRate); nicholas@755: } nicholas@755: // Create the required filters nicholas@755: var KFilter = offlineContext.createBiquadFilter(); nicholas@755: KFilter.type = "highshelf"; nicholas@755: KFilter.gain.value = 4; nicholas@755: KFilter.frequency.value = 1480; nicholas@755: nicholas@755: var HPFilter = offlineContext.createBiquadFilter(); nicholas@755: HPFilter.type = "highpass"; nicholas@755: HPFilter.Q.value = 0.707; nicholas@755: HPFilter.frequency.value = 60; nicholas@755: // copy Data into the process buffer nicholas@755: var processSource = offlineContext.createBufferSource(); nicholas@755: processSource.buffer = buffer; nicholas@755: nicholas@755: processSource.connect(KFilter); nicholas@755: KFilter.connect(HPFilter); nicholas@755: HPFilter.connect(offlineContext.destination); nicholas@755: processSource.start(); nicholas@755: offlineContext.startRendering().then(function(renderedBuffer) { nicholas@755: // Have the renderedBuffer information, now continue processing nicholas@755: switch(timescale) nicholas@755: { nicholas@755: case "I": nicholas@755: var blockEnergy = calculateProcessedLoudness(renderedBuffer, 400, 0.75); nicholas@755: // Apply the absolute gate nicholas@755: var loudness = calculateLoudnessFromChannelBlocks(blockEnergy); nicholas@755: var absgatedEnergy = new Array(blockEnergy.length); nicholas@755: for (var c=0; c= -70) nicholas@755: { nicholas@755: for (var c=0; c= relGateLevel) nicholas@755: { nicholas@755: for (var c=0; c= 4) {G = 1.41;} nicholas@755: sigma += blockEnergy[channel][i]*G; nicholas@755: } nicholas@755: loudness[i] = -0.691 + 10*Math.log10(sigma); nicholas@755: } nicholas@755: return loudness; nicholas@755: } nicholas@755: function calculateOverallLoudnessFromChannelBlocks(blockEnergy) nicholas@755: { nicholas@755: // Loudness nicholas@755: var summation = 0; nicholas@755: for (var channel = 0; channel < blockEnergy.length; channel++) nicholas@755: { nicholas@755: var G = 1.0; nicholas@755: if (channel >= 4) {G = 1.41;} nicholas@755: var sigma = 0; nicholas@755: for (var i=0; i