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