content/media/webaudio/test/blink/convolution-testing.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/webaudio/test/blink/convolution-testing.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,182 @@
     1.4 +var sampleRate = 44100.0;
     1.5 +
     1.6 +var renderLengthSeconds = 8;
     1.7 +var pulseLengthSeconds = 1;
     1.8 +var pulseLengthFrames = pulseLengthSeconds * sampleRate;
     1.9 +
    1.10 +function createSquarePulseBuffer(context, sampleFrameLength) {
    1.11 +    var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
    1.12 +
    1.13 +    var n = audioBuffer.length;
    1.14 +    var data = audioBuffer.getChannelData(0);
    1.15 +
    1.16 +    for (var i = 0; i < n; ++i)
    1.17 +        data[i] = 1;
    1.18 +
    1.19 +    return audioBuffer;
    1.20 +}
    1.21 +
    1.22 +// The triangle buffer holds the expected result of the convolution.
    1.23 +// It linearly ramps up from 0 to its maximum value (at the center)
    1.24 +// then linearly ramps down to 0.  The center value corresponds to the
    1.25 +// point where the two square pulses overlap the most.
    1.26 +function createTrianglePulseBuffer(context, sampleFrameLength) {
    1.27 +    var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
    1.28 +
    1.29 +    var n = audioBuffer.length;
    1.30 +    var halfLength = n / 2;
    1.31 +    var data = audioBuffer.getChannelData(0);
    1.32 +    
    1.33 +    for (var i = 0; i < halfLength; ++i)
    1.34 +        data[i] = i + 1;
    1.35 +
    1.36 +    for (var i = halfLength; i < n; ++i)
    1.37 +        data[i] = n - i - 1;
    1.38 +
    1.39 +    return audioBuffer;
    1.40 +}
    1.41 +
    1.42 +function log10(x) {
    1.43 +  return Math.log(x)/Math.LN10;
    1.44 +}
    1.45 +
    1.46 +function linearToDecibel(x) {
    1.47 +  return 20*log10(x);
    1.48 +}
    1.49 +
    1.50 +// Verify that the rendered result is very close to the reference
    1.51 +// triangular pulse.
    1.52 +function checkTriangularPulse(rendered, reference) {
    1.53 +    var match = true;
    1.54 +    var maxDelta = 0;
    1.55 +    var valueAtMaxDelta = 0;
    1.56 +    var maxDeltaIndex = 0;
    1.57 +
    1.58 +    for (var i = 0; i < reference.length; ++i) {
    1.59 +        var diff = rendered[i] - reference[i];
    1.60 +        var x = Math.abs(diff);
    1.61 +        if (x > maxDelta) {
    1.62 +            maxDelta = x;
    1.63 +            valueAtMaxDelta = reference[i];
    1.64 +            maxDeltaIndex = i;
    1.65 +        }
    1.66 +    }
    1.67 +
    1.68 +    // allowedDeviationFraction was determined experimentally.  It
    1.69 +    // is the threshold of the relative error at the maximum
    1.70 +    // difference between the true triangular pulse and the
    1.71 +    // rendered pulse.
    1.72 +    var allowedDeviationDecibels = -129.4;
    1.73 +    var maxDeviationDecibels = linearToDecibel(maxDelta / valueAtMaxDelta);
    1.74 +
    1.75 +    if (maxDeviationDecibels <= allowedDeviationDecibels) {
    1.76 +        testPassed("Triangular portion of convolution is correct.");
    1.77 +    } else {
    1.78 +        testFailed("Triangular portion of convolution is not correct.  Max deviation = " + maxDeviationDecibels + " dB at " + maxDeltaIndex);
    1.79 +        match = false;
    1.80 +    }
    1.81 +
    1.82 +    return match;
    1.83 +}        
    1.84 +
    1.85 +// Verify that the rendered data is close to zero for the first part
    1.86 +// of the tail.
    1.87 +function checkTail1(data, reference, breakpoint) {
    1.88 +    var isZero = true;
    1.89 +    var tail1Max = 0;
    1.90 +
    1.91 +    for (var i = reference.length; i < reference.length + breakpoint; ++i) {
    1.92 +        var mag = Math.abs(data[i]);
    1.93 +        if (mag > tail1Max) {
    1.94 +            tail1Max = mag;
    1.95 +        }
    1.96 +    }
    1.97 +
    1.98 +    // Let's find the peak of the reference (even though we know a
    1.99 +    // priori what it is).
   1.100 +    var refMax = 0;
   1.101 +    for (var i = 0; i < reference.length; ++i) {
   1.102 +      refMax = Math.max(refMax, Math.abs(reference[i]));
   1.103 +    }
   1.104 +
   1.105 +    // This threshold is experimentally determined by examining the
   1.106 +    // value of tail1MaxDecibels.
   1.107 +    var threshold1 = -129.7;
   1.108 +
   1.109 +    var tail1MaxDecibels = linearToDecibel(tail1Max/refMax);
   1.110 +    if (tail1MaxDecibels <= threshold1) {
   1.111 +        testPassed("First part of tail of convolution is sufficiently small.");
   1.112 +    } else {
   1.113 +        testFailed("First part of tail of convolution is not sufficiently small: " + tail1MaxDecibels + " dB");
   1.114 +        isZero = false;
   1.115 +    }
   1.116 +
   1.117 +    return isZero;
   1.118 +}
   1.119 +
   1.120 +// Verify that the second part of the tail of the convolution is
   1.121 +// exactly zero.
   1.122 +function checkTail2(data, reference, breakpoint) {
   1.123 +    var isZero = true;
   1.124 +    var tail2Max = 0;
   1.125 +    // For the second part of the tail, the maximum value should be
   1.126 +    // exactly zero.
   1.127 +    var threshold2 = 0;
   1.128 +    for (var i = reference.length + breakpoint; i < data.length; ++i) {
   1.129 +        if (Math.abs(data[i]) > 0) {
   1.130 +            isZero = false; 
   1.131 +            break;
   1.132 +        }
   1.133 +    }
   1.134 +
   1.135 +    if (isZero) {
   1.136 +        testPassed("Rendered signal after tail of convolution is silent.");
   1.137 +    } else {
   1.138 +        testFailed("Rendered signal after tail of convolution should be silent.");
   1.139 +    }
   1.140 +
   1.141 +    return isZero;
   1.142 +}
   1.143 +
   1.144 +function checkConvolvedResult(trianglePulse) {
   1.145 +    return function(event) {
   1.146 +        var renderedBuffer = event.renderedBuffer;
   1.147 +
   1.148 +        var referenceData = trianglePulse.getChannelData(0);
   1.149 +        var renderedData = renderedBuffer.getChannelData(0);
   1.150 +    
   1.151 +        var success = true;
   1.152 +    
   1.153 +        // Verify the triangular pulse is actually triangular.
   1.154 +
   1.155 +        success = success && checkTriangularPulse(renderedData, referenceData);
   1.156 +        
   1.157 +        // Make sure that portion after convolved portion is totally
   1.158 +        // silent.  But round-off prevents this from being completely
   1.159 +        // true.  At the end of the triangle, it should be close to
   1.160 +        // zero.  If we go farther out, it should be even closer and
   1.161 +        // eventually zero.
   1.162 +
   1.163 +        // For the tail of the convolution (where the result would be
   1.164 +        // theoretically zero), we partition the tail into two
   1.165 +        // parts.  The first is the at the beginning of the tail,
   1.166 +        // where we tolerate a small but non-zero value.  The second part is
   1.167 +        // farther along the tail where the result should be zero.
   1.168 +        
   1.169 +        // breakpoint is the point dividing the first two tail parts
   1.170 +        // we're looking at.  Experimentally determined.
   1.171 +        var breakpoint = 12800;
   1.172 +
   1.173 +        success = success && checkTail1(renderedData, referenceData, breakpoint);
   1.174 +        
   1.175 +        success = success && checkTail2(renderedData, referenceData, breakpoint);
   1.176 +        
   1.177 +        if (success) {
   1.178 +            testPassed("Test signal was correctly convolved.");
   1.179 +        } else {
   1.180 +            testFailed("Test signal was not correctly convolved.");
   1.181 +        }
   1.182 +
   1.183 +        finishJSTest();
   1.184 +    }
   1.185 +}

mercurial