|
1 // Helpers for Web Audio tests |
|
2 |
|
3 function expectException(func, exceptionCode) { |
|
4 var threw = false; |
|
5 try { |
|
6 func(); |
|
7 } catch (ex) { |
|
8 threw = true; |
|
9 ok(ex instanceof DOMException, "Expect a DOM exception"); |
|
10 is(ex.code, exceptionCode, "Expect the correct exception code"); |
|
11 } |
|
12 ok(threw, "The exception was thrown"); |
|
13 } |
|
14 |
|
15 function expectNoException(func) { |
|
16 var threw = false; |
|
17 try { |
|
18 func(); |
|
19 } catch (ex) { |
|
20 threw = true; |
|
21 } |
|
22 ok(!threw, "An exception was not thrown"); |
|
23 } |
|
24 |
|
25 function expectTypeError(func) { |
|
26 var threw = false; |
|
27 try { |
|
28 func(); |
|
29 } catch (ex) { |
|
30 threw = true; |
|
31 ok(ex instanceof TypeError, "Expect a TypeError"); |
|
32 } |
|
33 ok(threw, "The exception was thrown"); |
|
34 } |
|
35 |
|
36 function fuzzyCompare(a, b) { |
|
37 return Math.abs(a - b) < 9e-3; |
|
38 } |
|
39 |
|
40 function compareChannels(buf1, buf2, |
|
41 /*optional*/ length, |
|
42 /*optional*/ sourceOffset, |
|
43 /*optional*/ destOffset, |
|
44 /*optional*/ skipLengthCheck) { |
|
45 if (!skipLengthCheck) { |
|
46 is(buf1.length, buf2.length, "Channels must have the same length"); |
|
47 } |
|
48 sourceOffset = sourceOffset || 0; |
|
49 destOffset = destOffset || 0; |
|
50 if (length == undefined) { |
|
51 length = buf1.length - sourceOffset; |
|
52 } |
|
53 var difference = 0; |
|
54 var maxDifference = 0; |
|
55 var firstBadIndex = -1; |
|
56 for (var i = 0; i < length; ++i) { |
|
57 if (!fuzzyCompare(buf1[i + sourceOffset], buf2[i + destOffset])) { |
|
58 difference++; |
|
59 maxDifference = Math.max(maxDifference, Math.abs(buf1[i + sourceOffset] - buf2[i + destOffset])); |
|
60 if (firstBadIndex == -1) { |
|
61 firstBadIndex = i; |
|
62 } |
|
63 } |
|
64 }; |
|
65 |
|
66 is(difference, 0, "maxDifference: " + maxDifference + |
|
67 ", first bad index: " + firstBadIndex + |
|
68 " with test-data offset " + sourceOffset + " and expected-data offset " + |
|
69 destOffset + "; corresponding values " + buf1[firstBadIndex + sourceOffset] + |
|
70 " and " + buf2[firstBadIndex + destOffset] + " --- differences"); |
|
71 } |
|
72 |
|
73 function compareBuffers(got, expected) { |
|
74 if (got.numberOfChannels != expected.numberOfChannels) { |
|
75 is(got.numberOfChannels, expected.numberOfChannels, |
|
76 "Correct number of buffer channels"); |
|
77 return; |
|
78 } |
|
79 if (got.length != expected.length) { |
|
80 is(got.length, expected.length, |
|
81 "Correct buffer length"); |
|
82 return; |
|
83 } |
|
84 if (got.sampleRate != expected.sampleRate) { |
|
85 is(got.sampleRate, expected.sampleRate, |
|
86 "Correct sample rate"); |
|
87 return; |
|
88 } |
|
89 |
|
90 for (var i = 0; i < got.numberOfChannels; ++i) { |
|
91 compareChannels(got.getChannelData(i), expected.getChannelData(i), |
|
92 got.length, 0, 0, true); |
|
93 } |
|
94 } |
|
95 |
|
96 function getEmptyBuffer(context, length) { |
|
97 return context.createBuffer(gTest.numberOfChannels, length, context.sampleRate); |
|
98 } |
|
99 |
|
100 /** |
|
101 * This function assumes that the test file defines a single gTest variable with |
|
102 * the following properties and methods: |
|
103 * |
|
104 * + numberOfChannels: optional property which specifies the number of channels |
|
105 * in the output. The default value is 2. |
|
106 * + createGraph: mandatory method which takes a context object and does |
|
107 * everything needed in order to set up the Web Audio graph. |
|
108 * This function returns the node to be inspected. |
|
109 * + createGraphAsync: async version of createGraph. This function takes |
|
110 * a callback which should be called with an argument |
|
111 * set to the node to be inspected when the callee is |
|
112 * ready to proceed with the test. Either this function |
|
113 * or createGraph must be provided. |
|
114 * + createExpectedBuffers: optional method which takes a context object and |
|
115 * returns either one expected buffer or an array of |
|
116 * them, designating what is expected to be observed |
|
117 * in the output. If omitted, the output is expected |
|
118 * to be silence. All buffers must have the same |
|
119 * length, which must be a bufferSize supported by |
|
120 * ScriptProcessorNode. This function is guaranteed |
|
121 * to be called before createGraph. |
|
122 * + length: property equal to the total number of frames which we are waiting |
|
123 * to see in the output, mandatory if createExpectedBuffers is not |
|
124 * provided, in which case it must be a bufferSize supported by |
|
125 * ScriptProcessorNode (256, 512, 1024, 2048, 4096, 8192, or 16384). |
|
126 * If createExpectedBuffers is provided then this must be equal to |
|
127 * the number of expected buffers * the expected buffer length. |
|
128 * |
|
129 * + skipOfflineContextTests: optional. when true, skips running tests on an offline |
|
130 * context by circumventing testOnOfflineContext. |
|
131 */ |
|
132 function runTest() |
|
133 { |
|
134 function done() { |
|
135 SimpleTest.finish(); |
|
136 } |
|
137 |
|
138 SimpleTest.waitForExplicitFinish(); |
|
139 function runTestFunction () { |
|
140 if (!gTest.numberOfChannels) { |
|
141 gTest.numberOfChannels = 2; // default |
|
142 } |
|
143 |
|
144 var testLength; |
|
145 |
|
146 function runTestOnContext(context, callback, testOutput) { |
|
147 if (!gTest.createExpectedBuffers) { |
|
148 // Assume that the output is silence |
|
149 var expectedBuffers = getEmptyBuffer(context, gTest.length); |
|
150 } else { |
|
151 var expectedBuffers = gTest.createExpectedBuffers(context); |
|
152 } |
|
153 if (!(expectedBuffers instanceof Array)) { |
|
154 expectedBuffers = [expectedBuffers]; |
|
155 } |
|
156 var expectedFrames = 0; |
|
157 for (var i = 0; i < expectedBuffers.length; ++i) { |
|
158 is(expectedBuffers[i].numberOfChannels, gTest.numberOfChannels, |
|
159 "Correct number of channels for expected buffer " + i); |
|
160 expectedFrames += expectedBuffers[i].length; |
|
161 } |
|
162 if (gTest.length && gTest.createExpectedBuffers) { |
|
163 is(expectedFrames, gTest.length, "Correct number of expected frames"); |
|
164 } |
|
165 |
|
166 if (gTest.createGraphAsync) { |
|
167 gTest.createGraphAsync(context, function(nodeToInspect) { |
|
168 testOutput(nodeToInspect, expectedBuffers, callback); |
|
169 }); |
|
170 } else { |
|
171 testOutput(gTest.createGraph(context), expectedBuffers, callback); |
|
172 } |
|
173 } |
|
174 |
|
175 function testOnNormalContext(callback) { |
|
176 function testOutput(nodeToInspect, expectedBuffers, callback) { |
|
177 testLength = 0; |
|
178 var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels); |
|
179 nodeToInspect.connect(sp); |
|
180 sp.connect(context.destination); |
|
181 sp.onaudioprocess = function(e) { |
|
182 var expectedBuffer = expectedBuffers.shift(); |
|
183 testLength += expectedBuffer.length; |
|
184 compareBuffers(e.inputBuffer, expectedBuffer); |
|
185 if (expectedBuffers.length == 0) { |
|
186 sp.onaudioprocess = null; |
|
187 callback(); |
|
188 } |
|
189 }; |
|
190 } |
|
191 var context = new AudioContext(); |
|
192 runTestOnContext(context, callback, testOutput); |
|
193 } |
|
194 |
|
195 function testOnOfflineContext(callback, sampleRate) { |
|
196 function testOutput(nodeToInspect, expectedBuffers, callback) { |
|
197 nodeToInspect.connect(context.destination); |
|
198 context.oncomplete = function(e) { |
|
199 var samplesSeen = 0; |
|
200 while (expectedBuffers.length) { |
|
201 var expectedBuffer = expectedBuffers.shift(); |
|
202 is(e.renderedBuffer.numberOfChannels, expectedBuffer.numberOfChannels, |
|
203 "Correct number of input buffer channels"); |
|
204 for (var i = 0; i < e.renderedBuffer.numberOfChannels; ++i) { |
|
205 compareChannels(e.renderedBuffer.getChannelData(i), |
|
206 expectedBuffer.getChannelData(i), |
|
207 expectedBuffer.length, |
|
208 samplesSeen, |
|
209 undefined, |
|
210 true); |
|
211 } |
|
212 samplesSeen += expectedBuffer.length; |
|
213 } |
|
214 callback(); |
|
215 }; |
|
216 context.startRendering(); |
|
217 } |
|
218 |
|
219 var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, sampleRate); |
|
220 runTestOnContext(context, callback, testOutput); |
|
221 } |
|
222 |
|
223 testOnNormalContext(function() { |
|
224 if (!gTest.skipOfflineContextTests) { |
|
225 testOnOfflineContext(function() { |
|
226 testOnOfflineContext(done, 44100); |
|
227 }, 48000); |
|
228 } else { |
|
229 done(); |
|
230 } |
|
231 }); |
|
232 }; |
|
233 |
|
234 if (document.readyState !== 'complete') { |
|
235 addLoadEvent(runTestFunction); |
|
236 } else { |
|
237 runTestFunction(); |
|
238 } |
|
239 } |