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