Thu, 22 Jan 2015 13:21:57 +0100
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 }