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

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial