To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Revision:

root / loudness.js

History | View | Annotate | Download (4.77 KB)

1
/**
2
 *  loundess.js
3
 *  Loudness module for the Web Audio Evaluation Toolbox
4
 *  Allows for automatic calculation of loudness of Web Audio API Buffer objects,
5
 *         return gain values to correct for a target loudness or match loudness between
6
 *  multiple objects
7
 */
8

    
9
var interval_cal_loudness_event = null;
10

    
11
if (typeof OfflineAudioContext == "undefined"){
12
        var OfflineAudioContext = webkitOfflineAudioContext;
13
}
14

    
15
function calculateLoudness(buffer, timescale, target, offlineContext)
16
{
17
        // This function returns the EBU R 128 specification loudness model and sets the linear gain required to match -23 LUFS
18
        // buffer -> Web Audio API Buffer object
19
        // timescale -> M or Momentary (returns Array), S or Short (returns Array),
20
        //   I or Integrated (default, returns number)
21
        // target -> default is -23 LUFS but can be any LUFS measurement.
22
        
23
        if (buffer == undefined)
24
        {
25
                return 0;
26
        }
27
        if (timescale == undefined)
28
        {
29
                timescale = "I";
30
        }
31
        if (target == undefined)
32
        {
33
                target = -23;
34
        }
35
        if (offlineContext == undefined)
36
        {
37
                offlineContext = new OfflineAudioContext(audioContext.destination.channelCount, buffer.buffer.duration*audioContext.sampleRate, audioContext.sampleRate);
38
        }
39
        // Create the required filters
40
        var KFilter = offlineContext.createBiquadFilter();
41
        KFilter.type = "highshelf";
42
        KFilter.gain.value = 4;
43
        KFilter.frequency.value = 1500;
44
        
45
        var HPFilter = offlineContext.createBiquadFilter();
46
        HPFilter.type = "highpass";
47
        HPFilter.Q.value = 0.5;
48
        HPFilter.frequency.value = 38;
49
        // copy Data into the process buffer
50
        var processSource = offlineContext.createBufferSource();
51
        processSource.buffer = buffer.buffer;
52
        
53
        processSource.connect(KFilter);
54
        KFilter.connect(HPFilter);
55
        HPFilter.connect(offlineContext.destination);
56
        offlineContext.oncomplete = function(renderedBuffer) {
57
                // Have the renderedBuffer information, now continue processing
58
                if (typeof renderedBuffer.renderedBuffer == 'object') {
59
                        renderedBuffer = renderedBuffer.renderedBuffer;
60
                }
61
                switch(timescale)
62
                {
63
                case "I":
64
            // Calculate the Mean Squared of a signal
65
            var MS = calculateMeanSquared(renderedBuffer,0.4,0.75);
66
            // Calculate the Loudness of each block
67
            var MSL = calculateLoudnessFromBlocks(MS);
68
            // Get blocks from Absolute Gate
69
            var LK = loudnessGate(MSL,MS,-70);
70
            // Calculate Loudness
71
            var LK_gate = loudnessOfBlocks(LK);
72
            // Get blocks from Relative Gate
73
            var RK = loudnessGate(MSL,MS,LK_gate-10);
74
            var RK_gate = loudnessOfBlocks(RK);
75
            buffer.buffer.lufs = RK_gate;
76
                }
77
        buffer.ready();
78
        };
79
    processSource.start(0);
80
        offlineContext.startRendering();
81
}
82

    
83
function calculateMeanSquared(buffer,frame_dur,frame_overlap)
84
{
85
    frame_size = Math.floor(buffer.sampleRate*frame_dur);
86
    step_size = Math.floor(frame_size*(1.0-frame_overlap));
87
    num_frames = Math.floor((buffer.length-frame_size)/step_size);
88
    
89
    MS = Array(buffer.numberOfChannels);
90
    for (var c=0; c<buffer.numberOfChannels; c++)
91
    {
92
        MS[c] = new Float32Array(num_frames);
93
        var data = buffer.getChannelData(c);
94
        for (var no=0; no<num_frames; no++)
95
        {
96
            MS[c][no] = 0.0;
97
            for (var ptr=0; ptr<frame_size; ptr++)
98
            {
99
                var sample = data[no*step_size+ptr];
100
                MS[c][no] += sample*sample;
101
            }
102
            MS[c][no] /= frame_size;
103
        }
104
    }
105
    return MS;
106
}
107

    
108
function calculateLoudnessFromBlocks(blocks)
109
{
110
    var num_frames = blocks[0].length;
111
    var num_channels = blocks.length;
112
    var MSL = Array(num_frames);
113
    for (var n=0; n<num_frames; n++)
114
    {
115
        var sum = 0;
116
        for (var c=0; c<num_channels; c++)
117
        {
118
            var G = 1.0;
119
            if(G >= 3){G = 1.41;}
120
            sum += blocks[c][n]*G;
121
        }
122
        MSL[n] = -0.691 + 10*Math.log10(sum);
123
    }
124
    return MSL;
125
}
126

    
127
function loudnessGate(blocks,source,threshold)
128
{
129
    var num_frames = source[0].length;
130
    var num_channels = source.length;
131
    var LK = Array(num_channels);
132
    for (var c=0; c<num_channels; c++)
133
    {
134
        LK[c] = [];
135
    }
136
    
137
    for (var n=0; n<num_frames; n++)
138
    {
139
        if (blocks[n] > threshold)
140
        {
141
            for (var c=0; c<num_channels; c++)
142
            {
143
                LK[c].push(source[c][n]);
144
            }
145
        }
146
    }
147
    return LK;
148
}
149

    
150
function loudnessOfBlocks(blocks)
151
{
152
    var num_frames = blocks[0].length;
153
    var num_channels = blocks.length;
154
    var loudness = 0.0;
155
    for (var n=0; n<num_frames; n++)
156
    {
157
        var sum = 0;
158
        for (var c=0; c<num_channels; c++)
159
        {
160
            var G = 1.0;
161
            if(G >= 3){G = 1.41;}
162
            sum += blocks[c][n]*G;
163
        }
164
        sum /= num_frames;
165
        loudness += sum;
166
    }
167
    loudness = -0.691 + 10 * Math.log10(loudness);
168
    return loudness;
169
}