OLD | NEW |
---|---|
(Empty) | |
1 <!DOCTYPE html> | |
2 <html> | |
3 | |
4 <head> | |
5 <script src="resources/compatibility.js"></script> | |
6 <script src="resources/audio-testing.js"></script> | |
7 <script src="../resources/js-test.js"></script> | |
8 </head> | |
9 | |
10 <body> | |
11 <div id="description"></div> | |
12 <div id="console"></div> | |
13 <script> | |
14 description("Test panning model of StereoPannerNode."); | |
15 | |
16 window.jsTestIsAsync = true; | |
17 | |
18 var sampleRate = 44100; | |
19 | |
20 // Number of nodes to create for testing. | |
21 var nodesToCreate = 100; | |
22 | |
23 // Interval between the onset of each impulse. | |
24 var timeStep = 0.001; | |
25 | |
26 // Total render length. Should be long enough for all panner nodes. | |
27 var renderLength = timeStep * (nodesToCreate + 1); | |
28 | |
29 var impulse; | |
30 var impulseLength = Math.round(timeStep * sampleRate); | |
31 | |
32 // Pan step unit for each test trial. | |
33 var panStep = 2 / (nodesToCreate - 1); | |
34 | |
35 var sources = []; | |
36 var panners = []; | |
37 var panPositions = []; | |
38 var onsets = []; | |
39 | |
40 var ctx = new OfflineAudioContext(2, sampleRate * renderLength, sampleRate); | |
Raymond Toy
2014/11/12 18:06:52
Probably better to use "context" instead of "ctx".
hongchan
2014/11/12 22:01:42
Done.
| |
41 | |
42 // Calculates channel gains based on equal power panning model. | |
43 function getChannelGains (pan) { | |
44 var normalized = 0.5 * Math.PI * (pan * 0.5 + 0.5); | |
45 return { | |
46 gainL: Math.cos(normalized), | |
47 gainR: Math.sin(normalized) | |
48 }; | |
49 } | |
50 | |
51 function prepareTest() { | |
52 impulse = createImpulseBuffer(ctx, impulseLength); | |
53 | |
54 // Create multiple buffur source nodes and panner nodes for test trials. | |
Raymond Toy
2014/11/12 18:06:52
Typos: "buffur" -> buffer
Maybe instead of "for t
hongchan
2014/11/12 22:01:42
Done.
| |
55 // Each source node plays the same impulse buffer and goes through a | |
56 // panner node. Each trial is performed sequentially with the interval of | |
57 // 'timeStep' and a different pan position. | |
58 for (var i = 0; i < nodesToCreate; i++) { | |
59 sources[i] = ctx.createBufferSource(); | |
60 panners[i] = ctx.createStereoPanner(); | |
61 sources[i].connect(panners[i]); | |
62 panners[i].connect(ctx.destination); | |
63 | |
64 sources[i].buffer = impulse; | |
65 | |
66 // Moves the pan value for each panner by pan step unit. | |
67 panners[i].pan.value = panPositions[i] = panStep * i - 1; | |
68 | |
69 onsets[i] = timeStep * i; | |
70 sources[i].start(onsets[i]); | |
71 } | |
72 } | |
73 | |
74 // To verify the result, check if each source is an impulse turning at a | |
Raymond Toy
2014/11/12 18:06:52
Typo? "turning" -> "starting"?
hongchan
2014/11/12 22:01:42
Done.
| |
75 // different time and the rendered impulse has the expected gain. | |
76 function verifyResult(event) { | |
77 | |
78 var success = true; | |
79 | |
80 // The max error we allow between the rendered impulse and the | |
81 // expected value. This value is experimentally determined. Set | |
82 // to 0 to make the test fail to see what the actual error is. | |
83 var maxAllowedError = 1.3e-6; | |
84 | |
85 var chanL = event.renderedBuffer.getChannelData(0), | |
86 chanR = event.renderedBuffer.getChannelData(1); | |
87 | |
88 // Number of impulses found in the rendered result. | |
89 var impulseIndex = 0; | |
90 | |
91 // Max (relative) error and the index of the maxima for the left | |
92 // and right channels. | |
93 var maxErrorL = 0, | |
94 maxErrorR = 0, | |
95 maxErrorIndexL = 0, | |
96 maxErrorIndexR = 0; | |
97 | |
98 // Locations of where the impulses aren't at the expected locations. | |
99 var errors = []; | |
100 | |
101 for (var i = 0; i < chanL.length; i++) { | |
102 | |
103 // We assume that the left and right channels start at the same instant. | |
104 if (chanL[i] !== 0 || chanR[i] !== 0) { | |
105 | |
106 // Get amount of error between actual and expected gain. | |
107 var expected = getChannelGains(panPositions[impulseIndex]), | |
108 errorL = Math.abs(chanL[i] - expected.gainL), | |
109 errorR = Math.abs(chanR[i] - expected.gainR); | |
110 | |
111 if (errorL > maxErrorL) { | |
112 maxErrorL = errorL; | |
113 maxErrorIndexL = impulseIndex; | |
114 } | |
115 | |
116 if (errorR > maxErrorR) { | |
117 maxErrorR = errorR; | |
118 maxErrorIndexR = impulseIndex; | |
119 } | |
120 | |
121 // Keep track of the impulses that didn't show up where we expected | |
122 // them to be. | |
123 var expectedOffset = timeToSampleFrame(onsets[impulseIndex], sampleRat e); | |
124 if (i != expectedOffset) { | |
125 errors.push({ | |
126 actual: i, | |
127 expected: expectedOffset | |
128 }); | |
129 } | |
130 | |
131 impulseIndex++; | |
132 } | |
133 } | |
134 | |
135 if (impulseIndex === nodesToCreate) { | |
136 testPassed('Number of impulses matches the number of panner nodes.'); | |
137 } else { | |
138 testFailed('Number of impulses is incorrect. (Found ' + impulseIndex + ' but expected ' + nodesToCreate + ')'); | |
139 sucess = false; | |
140 } | |
141 | |
142 if (errors.length === 0) { | |
143 testPassed("All impulses at expected offsets."); | |
144 } else { | |
145 testFailed(errors.length + " timing errors found in " + nodesToCreate + " panner nodes."); | |
146 for (var i = 0; i < errors.length; i++) { | |
147 testFailed("Impulse at sample " + errors[i].actual + " but expected " + errors[i].expected); | |
148 } | |
149 success = false; | |
150 } | |
151 | |
152 if (maxErrorL <= maxAllowedError) { | |
153 testPassed("Left channel gain values are correct."); | |
154 } else { | |
155 testFailed("Left channel gain values are incorrect. Max error = " + max ErrorL + " at time " + onsets[maxErrorIndexL] + " (threshold = " + maxAllowedErr or + ")"); | |
156 success = false; | |
157 } | |
158 | |
159 if (maxErrorR <= maxAllowedError) { | |
160 testPassed("Right channel gain values are correct."); | |
161 } else { | |
162 testFailed("Right channel gain values are incorrect. Max error = " + ma xErrorR + " at time " + onsets[maxErrorIndexR] + " (threshold = " + maxAllowedEr ror + ")"); | |
163 success = false; | |
164 } | |
165 | |
166 if (success) | |
167 testPassed("StereoPannerNode test passed."); | |
168 else | |
169 testFailed("StereoPannerNode test failed."); | |
170 | |
171 finishJSTest(); | |
172 } | |
173 | |
174 prepareTest(); | |
175 ctx.oncomplete = verifyResult; | |
176 ctx.startRendering(); | |
177 | |
178 successfullyParsed = true; | |
179 </script> | |
180 </body> | |
181 | |
182 </html> | |
OLD | NEW |