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