OLD | NEW |
1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
2 | |
3 <html> | 2 <html> |
4 <head> | 3 <head> |
5 <script src="../../resources/testharness.js"></script> | 4 <title> |
6 <script src="../../resources/testharnessreport.js"></script> | 5 audionode-channel-rules.html |
7 <script src="../resources/audit-util.js"></script> | 6 </title> |
8 <script src="../resources/audit.js"></script> | 7 <script src="../../resources/testharness.js"></script> |
9 <script src="../resources/mixing-rules.js"></script> | 8 <script src="../../resources/testharnessreport.js"></script> |
10 </head> | 9 <script src="../resources/audit-util.js"></script> |
11 | 10 <script src="../resources/audit.js"></script> |
12 <body> | 11 <script src="../resources/mixing-rules.js"></script> |
13 | 12 </head> |
14 <script> | 13 <body> |
15 let audit = Audit.createTaskRunner(); | 14 <script id="layout-test-code"> |
16 let context = 0; | 15 let audit = Audit.createTaskRunner(); |
17 let sampleRate = 44100; | 16 let context = 0; |
18 let renderNumberOfChannels = 8; | 17 let sampleRate = 44100; |
19 let singleTestFrameLength = 8; | 18 let renderNumberOfChannels = 8; |
20 let testBuffers; | 19 let singleTestFrameLength = 8; |
21 | 20 let testBuffers; |
22 // A list of connections to an AudioNode input, each of which is to be used in | 21 |
23 // one or more specific test cases. Each element in the list is a string, with | 22 // A list of connections to an AudioNode input, each of which is to be |
24 // the number of connections corresponding to the length of the string, and each | 23 // used in one or more specific test cases. Each element in the list is a |
25 // character in the string is from '1' to '8' representing a 1 to 8 channel | 24 // string, with the number of connections corresponding to the length of |
26 // connection (from an AudioNode output). | 25 // the string, and each character in the string is from '1' to '8' |
27 | 26 // representing a 1 to 8 channel connection (from an AudioNode output). |
28 // For example, the string "128" means 3 connections, having 1, 2, and 8 | 27 |
29 // channels respectively. | 28 // For example, the string "128" means 3 connections, having 1, 2, and 8 |
30 | 29 // channels respectively. |
31 let connectionsList = [ | 30 |
32 '1', '2', '3', '4', '5', '6', '7', '8', '11', '12', '14', '18', '111', '122', | 31 let connectionsList = [ |
33 '123', '124', '128' | 32 '1', '2', '3', '4', '5', '6', '7', '8', '11', '12', '14', '18', '111', |
34 ]; | 33 '122', '123', '124', '128' |
35 | 34 ]; |
36 // A list of mixing rules, each of which will be tested against all of the | 35 |
37 // connections in connectionsList. | 36 // A list of mixing rules, each of which will be tested against all of the |
38 let mixingRulesList = [ | 37 // connections in connectionsList. |
39 {channelCount: 2, channelCountMode: 'max', channelInterpretation: 'speakers'}, | 38 let mixingRulesList = [ |
40 { | 39 { |
41 channelCount: 4, | 40 channelCount: 2, |
42 channelCountMode: 'clamped-max', | 41 channelCountMode: 'max', |
43 channelInterpretation: 'speakers' | 42 channelInterpretation: 'speakers' |
44 }, | 43 }, |
45 | 44 { |
46 // Test up-down-mix to some explicit speaker layouts. | 45 channelCount: 4, |
47 { | 46 channelCountMode: 'clamped-max', |
48 channelCount: 1, | 47 channelInterpretation: 'speakers' |
49 channelCountMode: 'explicit', | 48 }, |
50 channelInterpretation: 'speakers' | 49 |
51 }, | 50 // Test up-down-mix to some explicit speaker layouts. |
52 { | 51 { |
53 channelCount: 2, | 52 channelCount: 1, |
54 channelCountMode: 'explicit', | 53 channelCountMode: 'explicit', |
55 channelInterpretation: 'speakers' | 54 channelInterpretation: 'speakers' |
56 }, | 55 }, |
57 { | 56 { |
58 channelCount: 4, | 57 channelCount: 2, |
59 channelCountMode: 'explicit', | 58 channelCountMode: 'explicit', |
60 channelInterpretation: 'speakers' | 59 channelInterpretation: 'speakers' |
61 }, | 60 }, |
62 { | 61 { |
63 channelCount: 6, | 62 channelCount: 4, |
64 channelCountMode: 'explicit', | 63 channelCountMode: 'explicit', |
65 channelInterpretation: 'speakers' | 64 channelInterpretation: 'speakers' |
66 }, | 65 }, |
67 | 66 { |
68 {channelCount: 2, channelCountMode: 'max', channelInterpretation: 'discrete'}, | 67 channelCount: 6, |
69 { | 68 channelCountMode: 'explicit', |
70 channelCount: 4, | 69 channelInterpretation: 'speakers' |
71 channelCountMode: 'clamped-max', | 70 }, |
72 channelInterpretation: 'discrete' | 71 |
73 }, | 72 { |
74 { | 73 channelCount: 2, |
75 channelCount: 4, | 74 channelCountMode: 'max', |
76 channelCountMode: 'explicit', | 75 channelInterpretation: 'discrete' |
77 channelInterpretation: 'discrete' | 76 }, |
78 }, | 77 { |
79 { | 78 channelCount: 4, |
80 channelCount: 8, | 79 channelCountMode: 'clamped-max', |
81 channelCountMode: 'explicit', | 80 channelInterpretation: 'discrete' |
82 channelInterpretation: 'discrete' | 81 }, |
83 }, | 82 { |
84 ]; | 83 channelCount: 4, |
85 | 84 channelCountMode: 'explicit', |
86 let numberOfTests = mixingRulesList.length * connectionsList.length; | 85 channelInterpretation: 'discrete' |
87 | 86 }, |
88 // Print out the information for an individual test case. | 87 { |
89 function printTestInformation( | 88 channelCount: 8, |
90 testNumber, actualBuffer, expectedBuffer, frameLength, frameOffset) { | 89 channelCountMode: 'explicit', |
91 let actual = stringifyBuffer(actualBuffer, frameLength); | 90 channelInterpretation: 'discrete' |
92 let expected = stringifyBuffer(expectedBuffer, frameLength, frameOffset); | 91 }, |
93 debug('TEST CASE #' + testNumber + '\n'); | 92 ]; |
94 debug('actual channels:\n' + actual); | 93 |
95 debug('expected channels:\n' + expected); | 94 let numberOfTests = mixingRulesList.length * connectionsList.length; |
96 } | 95 |
97 | 96 // Print out the information for an individual test case. |
98 function scheduleTest( | 97 function printTestInformation( |
99 testNumber, connections, channelCount, channelCountMode, | 98 testNumber, actualBuffer, expectedBuffer, frameLength, frameOffset) { |
100 channelInterpretation) { | 99 let actual = stringifyBuffer(actualBuffer, frameLength); |
101 let mixNode = context.createGain(); | 100 let expected = |
102 mixNode.channelCount = channelCount; | 101 stringifyBuffer(expectedBuffer, frameLength, frameOffset); |
103 mixNode.channelCountMode = channelCountMode; | 102 debug('TEST CASE #' + testNumber + '\n'); |
104 mixNode.channelInterpretation = channelInterpretation; | 103 debug('actual channels:\n' + actual); |
105 mixNode.connect(context.destination); | 104 debug('expected channels:\n' + expected); |
106 | 105 } |
107 for (let i = 0; i < connections.length; ++i) { | 106 |
108 let connectionNumberOfChannels = | 107 function scheduleTest( |
109 connections.charCodeAt(i) - '0'.charCodeAt(0); | 108 testNumber, connections, channelCount, channelCountMode, |
110 | 109 channelInterpretation) { |
111 let source = context.createBufferSource(); | 110 let mixNode = context.createGain(); |
112 // Get a buffer with the right number of channels, converting from 1-based | 111 mixNode.channelCount = channelCount; |
113 // to 0-based index. | 112 mixNode.channelCountMode = channelCountMode; |
114 let buffer = testBuffers[connectionNumberOfChannels - 1]; | 113 mixNode.channelInterpretation = channelInterpretation; |
115 source.buffer = buffer; | 114 mixNode.connect(context.destination); |
116 source.connect(mixNode); | 115 |
117 | 116 for (let i = 0; i < connections.length; ++i) { |
118 // Start at the right offset. | 117 let connectionNumberOfChannels = |
119 let sampleFrameOffset = testNumber * singleTestFrameLength; | 118 connections.charCodeAt(i) - '0'.charCodeAt(0); |
120 let time = sampleFrameOffset / sampleRate; | 119 |
121 source.start(time); | 120 let source = context.createBufferSource(); |
122 } | 121 // Get a buffer with the right number of channels, converting from |
123 } | 122 // 1-based to 0-based index. |
124 | 123 let buffer = testBuffers[connectionNumberOfChannels - 1]; |
125 function checkTestResult( | 124 source.buffer = buffer; |
126 renderedBuffer, testNumber, connections, channelCount, channelCountMode, | 125 source.connect(mixNode); |
127 channelInterpretation, should) { | 126 |
128 let s = 'connections: ' + connections + ', ' + channelCountMode; | 127 // Start at the right offset. |
129 | 128 let sampleFrameOffset = testNumber * singleTestFrameLength; |
130 // channelCount is ignored in "max" mode. | 129 let time = sampleFrameOffset / sampleRate; |
131 if (channelCountMode == 'clamped-max' || channelCountMode == 'explicit') { | 130 source.start(time); |
132 s += '(' + channelCount + ')'; | 131 } |
133 } | 132 } |
134 | 133 |
135 s += ', ' + channelInterpretation; | 134 function checkTestResult( |
136 | 135 renderedBuffer, testNumber, connections, channelCount, |
137 let computedNumberOfChannels = | 136 channelCountMode, channelInterpretation, should) { |
138 computeNumberOfChannels(connections, channelCount, channelCountMode); | 137 let s = 'connections: ' + connections + ', ' + channelCountMode; |
139 | 138 |
140 // Create a zero-initialized silent AudioBuffer with computedNumberOfChannels. | 139 // channelCount is ignored in "max" mode. |
141 let destBuffer = context.createBuffer( | 140 if (channelCountMode == 'clamped-max' || |
142 computedNumberOfChannels, singleTestFrameLength, context.sampleRate); | 141 channelCountMode == 'explicit') { |
143 | 142 s += '(' + channelCount + ')'; |
144 // Mix all of the connections into the destination buffer. | 143 } |
145 for (let i = 0; i < connections.length; ++i) { | 144 |
146 let connectionNumberOfChannels = | 145 s += ', ' + channelInterpretation; |
147 connections.charCodeAt(i) - '0'.charCodeAt(0); | 146 |
148 let sourceBuffer = | 147 let computedNumberOfChannels = computeNumberOfChannels( |
149 testBuffers[connectionNumberOfChannels - 1]; // convert from 1-based to | 148 connections, channelCount, channelCountMode); |
150 // 0-based index | 149 |
151 | 150 // Create a zero-initialized silent AudioBuffer with |
152 if (channelInterpretation == 'speakers') { | 151 // computedNumberOfChannels. |
153 speakersSum(sourceBuffer, destBuffer); | 152 let destBuffer = context.createBuffer( |
154 } else if (channelInterpretation == 'discrete') { | 153 computedNumberOfChannels, singleTestFrameLength, |
155 discreteSum(sourceBuffer, destBuffer); | 154 context.sampleRate); |
156 } else { | 155 |
157 alert('Invalid channel interpretation!'); | 156 // Mix all of the connections into the destination buffer. |
158 } | 157 for (let i = 0; i < connections.length; ++i) { |
159 } | 158 let connectionNumberOfChannels = |
160 | 159 connections.charCodeAt(i) - '0'.charCodeAt(0); |
161 // Use this when debugging mixing rules. | 160 let sourceBuffer = |
162 // printTestInformation(testNumber, renderedBuffer, destBuffer, | 161 testBuffers[connectionNumberOfChannels - 1]; // convert from |
163 // singleTestFrameLength, sampleFrameOffset); | 162 // 1-based to |
164 | 163 // 0-based index |
165 // Validate that destBuffer matches the rendered output. We need to check | 164 |
166 // the rendered output at a specific sample-frame-offset corresponding to | 165 if (channelInterpretation == 'speakers') { |
167 // the specific test case we're checking for based on testNumber. | 166 speakersSum(sourceBuffer, destBuffer); |
168 | 167 } else if (channelInterpretation == 'discrete') { |
169 let sampleFrameOffset = testNumber * singleTestFrameLength; | 168 discreteSum(sourceBuffer, destBuffer); |
170 for (let c = 0; c < renderNumberOfChannels; ++c) { | 169 } else { |
171 let renderedData = renderedBuffer.getChannelData(c); | 170 alert('Invalid channel interpretation!'); |
172 for (let frame = 0; frame < singleTestFrameLength; ++frame) { | 171 } |
173 let renderedValue = renderedData[frame + sampleFrameOffset]; | 172 } |
174 | 173 |
175 let expectedValue = 0; | 174 // Use this when debugging mixing rules. |
176 if (c < destBuffer.numberOfChannels) { | 175 // printTestInformation(testNumber, renderedBuffer, destBuffer, |
177 let expectedData = destBuffer.getChannelData(c); | 176 // singleTestFrameLength, sampleFrameOffset); |
178 expectedValue = expectedData[frame]; | 177 |
179 } | 178 // Validate that destBuffer matches the rendered output. We need to |
180 | 179 // check the rendered output at a specific sample-frame-offset |
181 // We may need to add an epsilon in the comparison if we add more | 180 // corresponding to the specific test case we're checking for based on |
182 // test vectors. | 181 // testNumber. |
183 if (renderedValue != expectedValue) { | 182 |
184 let message = s + 'rendered: ' + renderedValue + ' expected: ' + | 183 let sampleFrameOffset = testNumber * singleTestFrameLength; |
185 expectedValue + ' channel: ' + c + ' frame: ' + frame; | 184 for (let c = 0; c < renderNumberOfChannels; ++c) { |
186 // testFailed(s); | 185 let renderedData = renderedBuffer.getChannelData(c); |
187 should(renderedValue, s).beEqualTo(expectedValue); | 186 for (let frame = 0; frame < singleTestFrameLength; ++frame) { |
188 return; | 187 let renderedValue = renderedData[frame + sampleFrameOffset]; |
189 } | 188 |
190 } | 189 let expectedValue = 0; |
191 } | 190 if (c < destBuffer.numberOfChannels) { |
192 | 191 let expectedData = destBuffer.getChannelData(c); |
193 should(true, s).beTrue(); | 192 expectedValue = expectedData[frame]; |
194 } | 193 } |
195 | 194 |
196 function checkResult(buffer, should) { | 195 // We may need to add an epsilon in the comparison if we add more |
197 // Sanity check result. | 196 // test vectors. |
198 should(buffer.length, 'Rendered number of frames') | 197 if (renderedValue != expectedValue) { |
199 .beEqualTo(numberOfTests * singleTestFrameLength); | 198 let message = s + 'rendered: ' + renderedValue + |
200 should(buffer.numberOfChannels, 'Rendered number of channels') | 199 ' expected: ' + expectedValue + ' channel: ' + c + |
201 .beEqualTo(renderNumberOfChannels); | 200 ' frame: ' + frame; |
202 | 201 // testFailed(s); |
203 // Check all the tests. | 202 should(renderedValue, s).beEqualTo(expectedValue); |
204 let testNumber = 0; | 203 return; |
205 for (let m = 0; m < mixingRulesList.length; ++m) { | 204 } |
206 let mixingRules = mixingRulesList[m]; | 205 } |
207 for (let i = 0; i < connectionsList.length; ++i, ++testNumber) { | 206 } |
208 checkTestResult( | 207 |
209 buffer, testNumber, connectionsList[i], mixingRules.channelCount, | 208 should(true, s).beTrue(); |
210 mixingRules.channelCountMode, mixingRules.channelInterpretation, | 209 } |
211 should); | 210 |
212 } | 211 function checkResult(buffer, should) { |
213 } | 212 // Sanity check result. |
214 } | 213 should(buffer.length, 'Rendered number of frames') |
215 | 214 .beEqualTo(numberOfTests * singleTestFrameLength); |
216 audit.define({ | 215 should(buffer.numberOfChannels, 'Rendered number of channels') |
217 label: 'test', | 216 .beEqualTo(renderNumberOfChannels); |
218 description: 'Channel mixing rules for AudioNodes' | 217 |
219 }, function(task, should) { | 218 // Check all the tests. |
220 | 219 let testNumber = 0; |
221 // Create 8-channel offline audio context. Each test will render 8 | 220 for (let m = 0; m < mixingRulesList.length; ++m) { |
222 // sample-frames starting at sample-frame position testNumber * 8. | 221 let mixingRules = mixingRulesList[m]; |
223 let totalFrameLength = numberOfTests * singleTestFrameLength; | 222 for (let i = 0; i < connectionsList.length; ++i, ++testNumber) { |
224 context = new OfflineAudioContext( | 223 checkTestResult( |
225 renderNumberOfChannels, totalFrameLength, sampleRate); | 224 buffer, testNumber, connectionsList[i], |
226 | 225 mixingRules.channelCount, mixingRules.channelCountMode, |
227 // Set destination to discrete mixing. | 226 mixingRules.channelInterpretation, should); |
228 context.destination.channelCount = renderNumberOfChannels; | 227 } |
229 context.destination.channelCountMode = 'explicit'; | 228 } |
230 context.destination.channelInterpretation = 'discrete'; | 229 } |
231 | 230 |
232 // Create test buffers from 1 to 8 channels. | 231 audit.define( |
233 testBuffers = new Array(); | 232 {label: 'test', description: 'Channel mixing rules for AudioNodes'}, |
234 for (let i = 0; i < renderNumberOfChannels; ++i) { | 233 function(task, should) { |
235 testBuffers[i] = | 234 |
236 createShiftedImpulseBuffer(context, i + 1, singleTestFrameLength); | 235 // Create 8-channel offline audio context. Each test will render 8 |
237 } | 236 // sample-frames starting at sample-frame position testNumber * 8. |
238 | 237 let totalFrameLength = numberOfTests * singleTestFrameLength; |
239 // Schedule all the tests. | 238 context = new OfflineAudioContext( |
240 let testNumber = 0; | 239 renderNumberOfChannels, totalFrameLength, sampleRate); |
241 for (let m = 0; m < mixingRulesList.length; ++m) { | 240 |
242 let mixingRules = mixingRulesList[m]; | 241 // Set destination to discrete mixing. |
243 for (let i = 0; i < connectionsList.length; ++i, ++testNumber) { | 242 context.destination.channelCount = renderNumberOfChannels; |
244 scheduleTest( | 243 context.destination.channelCountMode = 'explicit'; |
245 testNumber, connectionsList[i], mixingRules.channelCount, | 244 context.destination.channelInterpretation = 'discrete'; |
246 mixingRules.channelCountMode, mixingRules.channelInterpretation); | 245 |
247 } | 246 // Create test buffers from 1 to 8 channels. |
248 } | 247 testBuffers = new Array(); |
249 | 248 for (let i = 0; i < renderNumberOfChannels; ++i) { |
250 // Render then check results. | 249 testBuffers[i] = createShiftedImpulseBuffer( |
251 // context.oncomplete = checkResult; | 250 context, i + 1, singleTestFrameLength); |
252 context.startRendering().then(buffer => { | 251 } |
253 checkResult(buffer, should); | 252 |
254 task.done(); | 253 // Schedule all the tests. |
255 }); | 254 let testNumber = 0; |
256 ; | 255 for (let m = 0; m < mixingRulesList.length; ++m) { |
257 }); | 256 let mixingRules = mixingRulesList[m]; |
258 | 257 for (let i = 0; i < connectionsList.length; ++i, ++testNumber) { |
259 audit.run(); | 258 scheduleTest( |
260 | 259 testNumber, connectionsList[i], mixingRules.channelCount, |
261 </script> | 260 mixingRules.channelCountMode, |
262 | 261 mixingRules.channelInterpretation); |
263 </body> | 262 } |
| 263 } |
| 264 |
| 265 // Render then check results. |
| 266 // context.oncomplete = checkResult; |
| 267 context.startRendering().then(buffer => { |
| 268 checkResult(buffer, should); |
| 269 task.done(); |
| 270 }); |
| 271 ; |
| 272 }); |
| 273 |
| 274 audit.run(); |
| 275 </script> |
| 276 </body> |
264 </html> | 277 </html> |
OLD | NEW |