Mercurial > hg > webaudioevaluationtool
view js/loudness.js @ 2376:c41caaa96633
Some fixes for #90. Also a failsafe loop if the server never responds with meaningul information from saves (for instance, running only on apache or basic http servers). More changes to pythonServer for python 3.5. Please check if still valid on 2.7
author | Nicholas Jillings <nicholas.jillings@mail.bcu.ac.uk> |
---|---|
date | Thu, 19 May 2016 10:44:19 +0100 |
parents | 760719986df3 |
children | aca96a5183be |
line wrap: on
line source
/** * loundess.js * Loudness module for the Web Audio Evaluation Toolbox * Allows for automatic calculation of loudness of Web Audio API Buffer objects, * return gain values to correct for a target loudness or match loudness between * multiple objects */ var interval_cal_loudness_event = null; if (typeof OfflineAudioContext == "undefined"){ var OfflineAudioContext = webkitOfflineAudioContext; } function calculateLoudness(buffer, timescale, target, offlineContext) { // This function returns the EBU R 128 specification loudness model and sets the linear gain required to match -23 LUFS // buffer -> Web Audio API Buffer object // timescale -> M or Momentary (returns Array), S or Short (returns Array), // I or Integrated (default, returns number) // target -> default is -23 LUFS but can be any LUFS measurement. if (buffer == undefined) { return 0; } if (timescale == undefined) { timescale = "I"; } if (target == undefined) { target = -23; } if (offlineContext == undefined) { offlineContext = new OfflineAudioContext(audioContext.destination.channelCount, buffer.buffer.duration*audioContext.sampleRate, audioContext.sampleRate); } // Create the required filters var KFilter = offlineContext.createBiquadFilter(); KFilter.type = "highshelf"; KFilter.gain.value = 4; KFilter.frequency.value = 1500; var HPFilter = offlineContext.createBiquadFilter(); HPFilter.type = "highpass"; HPFilter.Q.value = 0.5; HPFilter.frequency.value = 38; // copy Data into the process buffer var processSource = offlineContext.createBufferSource(); processSource.buffer = buffer.buffer; processSource.connect(KFilter); KFilter.connect(HPFilter); HPFilter.connect(offlineContext.destination); offlineContext.oncomplete = function(renderedBuffer) { // Have the renderedBuffer information, now continue processing if (typeof renderedBuffer.renderedBuffer == 'object') { renderedBuffer = renderedBuffer.renderedBuffer; } switch(timescale) { case "I": // Calculate the Mean Squared of a signal var MS = calculateMeanSquared(renderedBuffer,0.4,0.75); // Calculate the Loudness of each block var MSL = calculateLoudnessFromBlocks(MS); // Get blocks from Absolute Gate var LK = loudnessGate(MSL,MS,-70); // Calculate Loudness var LK_gate = loudnessOfBlocks(LK); // Get blocks from Relative Gate var RK = loudnessGate(MSL,MS,LK_gate-10); var RK_gate = loudnessOfBlocks(RK); buffer.buffer.lufs = RK_gate; } buffer.ready(); }; processSource.start(0); offlineContext.startRendering(); } function calculateMeanSquared(buffer,frame_dur,frame_overlap) { frame_size = Math.floor(buffer.sampleRate*frame_dur); step_size = Math.floor(frame_size*(1.0-frame_overlap)); num_frames = Math.floor((buffer.length-frame_size)/step_size); MS = Array(buffer.numberOfChannels); for (var c=0; c<buffer.numberOfChannels; c++) { MS[c] = new Float32Array(num_frames); var data = buffer.getChannelData(c); for (var no=0; no<num_frames; no++) { MS[c][no] = 0.0; for (var ptr=0; ptr<frame_size; ptr++) { var sample = data[no*step_size+ptr]; MS[c][no] += sample*sample; } MS[c][no] /= frame_size; } } return MS; } function calculateLoudnessFromBlocks(blocks) { var num_frames = blocks[0].length; var num_channels = blocks.length; var MSL = Array(num_frames); for (var n=0; n<num_frames; n++) { var sum = 0; for (var c=0; c<num_channels; c++) { var G = 1.0; if(G >= 3){G = 1.41;} sum += blocks[c][n]*G; } MSL[n] = -0.691 + 10*Math.log10(sum); } return MSL; } function loudnessGate(blocks,source,threshold) { var num_frames = source[0].length; var num_channels = source.length; var LK = Array(num_channels); for (var c=0; c<num_channels; c++) { LK[c] = []; } for (var n=0; n<num_frames; n++) { if (blocks[n] > threshold) { for (var c=0; c<num_channels; c++) { LK[c].push(source[c][n]); } } } return LK; } function loudnessOfBlocks(blocks) { var num_frames = blocks[0].length; var num_channels = blocks.length; var loudness = 0.0; for (var n=0; n<num_frames; n++) { var sum = 0; for (var c=0; c<num_channels; c++) { var G = 1.0; if(G >= 3){G = 1.41;} sum += blocks[c][n]*G; } sum /= num_frames; loudness += sum; } loudness = -0.691 + 10 * Math.log10(loudness); return loudness; }