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

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 var sampleRate = 44100.0;
     3 var renderLengthSeconds = 8;
     4 var pulseLengthSeconds = 1;
     5 var pulseLengthFrames = pulseLengthSeconds * sampleRate;
     7 function createSquarePulseBuffer(context, sampleFrameLength) {
     8     var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
    10     var n = audioBuffer.length;
    11     var data = audioBuffer.getChannelData(0);
    13     for (var i = 0; i < n; ++i)
    14         data[i] = 1;
    16     return audioBuffer;
    17 }
    19 // The triangle buffer holds the expected result of the convolution.
    20 // It linearly ramps up from 0 to its maximum value (at the center)
    21 // then linearly ramps down to 0.  The center value corresponds to the
    22 // point where the two square pulses overlap the most.
    23 function createTrianglePulseBuffer(context, sampleFrameLength) {
    24     var audioBuffer = context.createBuffer(1, sampleFrameLength, context.sampleRate);
    26     var n = audioBuffer.length;
    27     var halfLength = n / 2;
    28     var data = audioBuffer.getChannelData(0);
    30     for (var i = 0; i < halfLength; ++i)
    31         data[i] = i + 1;
    33     for (var i = halfLength; i < n; ++i)
    34         data[i] = n - i - 1;
    36     return audioBuffer;
    37 }
    39 function log10(x) {
    40   return Math.log(x)/Math.LN10;
    41 }
    43 function linearToDecibel(x) {
    44   return 20*log10(x);
    45 }
    47 // Verify that the rendered result is very close to the reference
    48 // triangular pulse.
    49 function checkTriangularPulse(rendered, reference) {
    50     var match = true;
    51     var maxDelta = 0;
    52     var valueAtMaxDelta = 0;
    53     var maxDeltaIndex = 0;
    55     for (var i = 0; i < reference.length; ++i) {
    56         var diff = rendered[i] - reference[i];
    57         var x = Math.abs(diff);
    58         if (x > maxDelta) {
    59             maxDelta = x;
    60             valueAtMaxDelta = reference[i];
    61             maxDeltaIndex = i;
    62         }
    63     }
    65     // allowedDeviationFraction was determined experimentally.  It
    66     // is the threshold of the relative error at the maximum
    67     // difference between the true triangular pulse and the
    68     // rendered pulse.
    69     var allowedDeviationDecibels = -129.4;
    70     var maxDeviationDecibels = linearToDecibel(maxDelta / valueAtMaxDelta);
    72     if (maxDeviationDecibels <= allowedDeviationDecibels) {
    73         testPassed("Triangular portion of convolution is correct.");
    74     } else {
    75         testFailed("Triangular portion of convolution is not correct.  Max deviation = " + maxDeviationDecibels + " dB at " + maxDeltaIndex);
    76         match = false;
    77     }
    79     return match;
    80 }        
    82 // Verify that the rendered data is close to zero for the first part
    83 // of the tail.
    84 function checkTail1(data, reference, breakpoint) {
    85     var isZero = true;
    86     var tail1Max = 0;
    88     for (var i = reference.length; i < reference.length + breakpoint; ++i) {
    89         var mag = Math.abs(data[i]);
    90         if (mag > tail1Max) {
    91             tail1Max = mag;
    92         }
    93     }
    95     // Let's find the peak of the reference (even though we know a
    96     // priori what it is).
    97     var refMax = 0;
    98     for (var i = 0; i < reference.length; ++i) {
    99       refMax = Math.max(refMax, Math.abs(reference[i]));
   100     }
   102     // This threshold is experimentally determined by examining the
   103     // value of tail1MaxDecibels.
   104     var threshold1 = -129.7;
   106     var tail1MaxDecibels = linearToDecibel(tail1Max/refMax);
   107     if (tail1MaxDecibels <= threshold1) {
   108         testPassed("First part of tail of convolution is sufficiently small.");
   109     } else {
   110         testFailed("First part of tail of convolution is not sufficiently small: " + tail1MaxDecibels + " dB");
   111         isZero = false;
   112     }
   114     return isZero;
   115 }
   117 // Verify that the second part of the tail of the convolution is
   118 // exactly zero.
   119 function checkTail2(data, reference, breakpoint) {
   120     var isZero = true;
   121     var tail2Max = 0;
   122     // For the second part of the tail, the maximum value should be
   123     // exactly zero.
   124     var threshold2 = 0;
   125     for (var i = reference.length + breakpoint; i < data.length; ++i) {
   126         if (Math.abs(data[i]) > 0) {
   127             isZero = false; 
   128             break;
   129         }
   130     }
   132     if (isZero) {
   133         testPassed("Rendered signal after tail of convolution is silent.");
   134     } else {
   135         testFailed("Rendered signal after tail of convolution should be silent.");
   136     }
   138     return isZero;
   139 }
   141 function checkConvolvedResult(trianglePulse) {
   142     return function(event) {
   143         var renderedBuffer = event.renderedBuffer;
   145         var referenceData = trianglePulse.getChannelData(0);
   146         var renderedData = renderedBuffer.getChannelData(0);
   148         var success = true;
   150         // Verify the triangular pulse is actually triangular.
   152         success = success && checkTriangularPulse(renderedData, referenceData);
   154         // Make sure that portion after convolved portion is totally
   155         // silent.  But round-off prevents this from being completely
   156         // true.  At the end of the triangle, it should be close to
   157         // zero.  If we go farther out, it should be even closer and
   158         // eventually zero.
   160         // For the tail of the convolution (where the result would be
   161         // theoretically zero), we partition the tail into two
   162         // parts.  The first is the at the beginning of the tail,
   163         // where we tolerate a small but non-zero value.  The second part is
   164         // farther along the tail where the result should be zero.
   166         // breakpoint is the point dividing the first two tail parts
   167         // we're looking at.  Experimentally determined.
   168         var breakpoint = 12800;
   170         success = success && checkTail1(renderedData, referenceData, breakpoint);
   172         success = success && checkTail2(renderedData, referenceData, breakpoint);
   174         if (success) {
   175             testPassed("Test signal was correctly convolved.");
   176         } else {
   177             testFailed("Test signal was not correctly convolved.");
   178         }
   180         finishJSTest();
   181     }
   182 }

mercurial