giuliomoro@1088: /** giuliomoro@1088: * loundess.js giuliomoro@1088: * Loudness module for the Web Audio Evaluation Toolbox giuliomoro@1088: * Allows for automatic calculation of loudness of Web Audio API Buffer objects, giuliomoro@1088: * return gain values to correct for a target loudness or match loudness between giuliomoro@1088: * multiple objects giuliomoro@1088: */ giuliomoro@1088: giuliomoro@1088: var interval_cal_loudness_event = null; giuliomoro@1088: giuliomoro@1088: if (typeof OfflineAudioContext == "undefined"){ giuliomoro@1088: var OfflineAudioContext = webkitOfflineAudioContext; giuliomoro@1088: } giuliomoro@1088: giuliomoro@1088: function calculateLoudness(buffer, timescale, target, offlineContext) giuliomoro@1088: { giuliomoro@1088: // This function returns the EBU R 128 specification loudness model and sets the linear gain required to match -23 LUFS giuliomoro@1088: // buffer -> Web Audio API Buffer object giuliomoro@1088: // timescale -> M or Momentary (returns Array), S or Short (returns Array), giuliomoro@1088: // I or Integrated (default, returns number) giuliomoro@1088: // target -> default is -23 LUFS but can be any LUFS measurement. giuliomoro@1088: giuliomoro@1088: if (buffer == undefined) giuliomoro@1088: { giuliomoro@1088: return 0; giuliomoro@1088: } giuliomoro@1088: if (timescale == undefined) giuliomoro@1088: { giuliomoro@1088: timescale = "I"; giuliomoro@1088: } giuliomoro@1088: if (target == undefined) giuliomoro@1088: { giuliomoro@1088: target = -23; giuliomoro@1088: } giuliomoro@1088: if (offlineContext == undefined) giuliomoro@1088: { giuliomoro@1088: offlineContext = new OfflineAudioContext(audioContext.destination.channelCount, buffer.buffer.duration*audioContext.sampleRate, audioContext.sampleRate); giuliomoro@1088: } giuliomoro@1088: // Create the required filters giuliomoro@1088: var KFilter = offlineContext.createBiquadFilter(); giuliomoro@1088: KFilter.type = "highshelf"; giuliomoro@1088: KFilter.gain.value = 4; giuliomoro@1088: KFilter.frequency.value = 1500; giuliomoro@1088: giuliomoro@1088: var HPFilter = offlineContext.createBiquadFilter(); giuliomoro@1088: HPFilter.type = "highpass"; giuliomoro@1088: HPFilter.Q.value = 0.5; giuliomoro@1088: HPFilter.frequency.value = 38; giuliomoro@1088: // copy Data into the process buffer giuliomoro@1088: var processSource = offlineContext.createBufferSource(); giuliomoro@1088: processSource.buffer = buffer.buffer; giuliomoro@1088: giuliomoro@1088: processSource.connect(KFilter); giuliomoro@1088: KFilter.connect(HPFilter); giuliomoro@1088: HPFilter.connect(offlineContext.destination); giuliomoro@1088: processSource.start(); giuliomoro@1088: offlineContext.oncomplete = function(renderedBuffer) { giuliomoro@1088: // Have the renderedBuffer information, now continue processing giuliomoro@1088: if (typeof renderedBuffer.renderedBuffer == 'object') { giuliomoro@1088: renderedBuffer = renderedBuffer.renderedBuffer; giuliomoro@1088: } giuliomoro@1088: switch(timescale) giuliomoro@1088: { giuliomoro@1088: case "I": giuliomoro@1088: // Calculate the Mean Squared of a signal giuliomoro@1088: var MS = calculateMeanSquared(renderedBuffer,0.4,0.75); giuliomoro@1088: // Calculate the Loudness of each block giuliomoro@1088: var MSL = calculateLoudnessFromBlocks(MS); giuliomoro@1088: // Get blocks from Absolute Gate giuliomoro@1088: var LK = loudnessGate(MSL,MS,-70); giuliomoro@1088: // Calculate Loudness giuliomoro@1088: var LK_gate = loudnessOfBlocks(LK); giuliomoro@1088: // Get blocks from Relative Gate giuliomoro@1088: var RK = loudnessGate(MSL,MS,LK_gate-10); giuliomoro@1088: var RK_gate = loudnessOfBlocks(RK); giuliomoro@1088: buffer.buffer.lufs = RK_gate; giuliomoro@1088: } giuliomoro@1088: buffer.ready(); giuliomoro@1088: }; giuliomoro@1088: offlineContext.startRendering(); giuliomoro@1088: } giuliomoro@1088: giuliomoro@1088: function calculateMeanSquared(buffer,frame_dur,frame_overlap) giuliomoro@1088: { giuliomoro@1088: frame_size = Math.floor(buffer.sampleRate*frame_dur); giuliomoro@1088: step_size = Math.floor(frame_size*(1.0-frame_overlap)); giuliomoro@1088: num_frames = Math.floor((buffer.length-frame_size)/step_size); giuliomoro@1088: giuliomoro@1088: MS = Array(buffer.numberOfChannels); giuliomoro@1088: for (var c=0; c= 3){G = 1.41;} giuliomoro@1088: sum += blocks[c][n]*G; giuliomoro@1088: } giuliomoro@1088: MSL[n] = -0.691 + 10*Math.log10(sum); giuliomoro@1088: } giuliomoro@1088: return MSL; giuliomoro@1088: } giuliomoro@1088: giuliomoro@1088: function loudnessGate(blocks,source,threshold) giuliomoro@1088: { giuliomoro@1088: var num_frames = source[0].length; giuliomoro@1088: var num_channels = source.length; giuliomoro@1088: var LK = Array(num_channels); giuliomoro@1088: for (var c=0; c threshold) giuliomoro@1088: { giuliomoro@1088: for (var c=0; c= 3){G = 1.41;} giuliomoro@1088: sum += blocks[c][n]*G; giuliomoro@1088: } giuliomoro@1088: sum /= num_frames; giuliomoro@1088: loudness += sum; giuliomoro@1088: } giuliomoro@1088: loudness = -0.691 + 10 * Math.log10(loudness); giuliomoro@1088: return loudness; giuliomoro@1088: }