| OLD | NEW |
| 1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
| 2 | 2 |
| 3 <html> | 3 <html> |
| 4 <head> | 4 <head> |
| 5 <script src="../resources/js-test.js"></script> | 5 <script src="../resources/js-test.js"></script> |
| 6 <script src="resources/compatibility.js"></script> | 6 <script src="resources/compatibility.js"></script> |
| 7 <script type="text/javascript" src="resources/audio-testing.js"></script> | 7 <script src="resources/audio-testing.js"></script> |
| 8 <script src="resources/mixing-rules.js"></script> |
| 8 </head> | 9 </head> |
| 9 | 10 |
| 10 <body> | 11 <body> |
| 11 <div id="description"></div> | |
| 12 <div id="console"></div> | |
| 13 | 12 |
| 14 <script> | 13 <script> |
| 15 description("Channel mixing rules for AudioNodes."); | 14 description("Channel mixing rules for AudioNodes."); |
| 16 | 15 |
| 17 var context = 0; | 16 var context = 0; |
| 18 var sampleRate = 44100; | 17 var sampleRate = 44100; |
| 19 var renderNumberOfChannels = 8; | 18 var renderNumberOfChannels = 8; |
| 20 var singleTestFrameLength = 8; | 19 var singleTestFrameLength = 8; |
| 21 var testBuffers; | 20 var testBuffers; |
| 22 | 21 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 38 {channelCount: 6, channelCountMode: "explicit", channelInterpretation: "spea
kers"}, | 37 {channelCount: 6, channelCountMode: "explicit", channelInterpretation: "spea
kers"}, |
| 39 | 38 |
| 40 {channelCount: 2, channelCountMode: "max", channelInterpretation: "discrete"
}, | 39 {channelCount: 2, channelCountMode: "max", channelInterpretation: "discrete"
}, |
| 41 {channelCount: 4, channelCountMode: "clamped-max", channelInterpretation: "d
iscrete"}, | 40 {channelCount: 4, channelCountMode: "clamped-max", channelInterpretation: "d
iscrete"}, |
| 42 {channelCount: 4, channelCountMode: "explicit", channelInterpretation: "disc
rete"}, | 41 {channelCount: 4, channelCountMode: "explicit", channelInterpretation: "disc
rete"}, |
| 43 {channelCount: 8, channelCountMode: "explicit", channelInterpretation: "disc
rete"}, | 42 {channelCount: 8, channelCountMode: "explicit", channelInterpretation: "disc
rete"}, |
| 44 ]; | 43 ]; |
| 45 | 44 |
| 46 var numberOfTests = mixingRulesList.length * connectionsList.length; | 45 var numberOfTests = mixingRulesList.length * connectionsList.length; |
| 47 | 46 |
| 48 // Create an n-channel buffer, with all sample data zero except for a shifted im
pulse. | 47 // Print out the information for an individual test case. |
| 49 // The impulse position depends on the channel index. | 48 function printTestInformation(testNumber, actualBuffer, expectedBuffer, frameLen
gth, frameOffset) { |
| 50 // For example, for a 4-channel buffer: | 49 var actual = stringifyBuffer(actualBuffer, frameLength); |
| 51 // channel0: 1 0 0 0 0 0 0 0 | 50 var expected = stringifyBuffer(expectedBuffer, frameLength, frameOffset); |
| 52 // channel1: 0 1 0 0 0 0 0 0 | 51 debug('TEST CASE #' + testNumber + '\n'); |
| 53 // channel2: 0 0 1 0 0 0 0 0 | 52 debug('actual channels:\n' + actual); |
| 54 // channel3: 0 0 0 1 0 0 0 0 | 53 debug('expected channels:\n' + expected); |
| 55 function createTestBuffer(numberOfChannels) { | |
| 56 var buffer = context.createBuffer(numberOfChannels, singleTestFrameLength, c
ontext.sampleRate); | |
| 57 for (var i = 0; i < numberOfChannels; ++i) { | |
| 58 var data = buffer.getChannelData(i); | |
| 59 data[i] = 1; | |
| 60 } | |
| 61 return buffer; | |
| 62 } | |
| 63 | |
| 64 // Discrete channel interpretation mixing: | |
| 65 // https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#UpMix | |
| 66 // up-mix by filling channels until they run out then ignore remaining dest chan
nels. | |
| 67 // down-mix by filling as many channels as possible, then dropping remaining sou
rce channels. | |
| 68 function discreteSum(sourceBuffer, destBuffer) { | |
| 69 if (sourceBuffer.length != destBuffer.length) { | |
| 70 alert("discreteSum(): invalid AudioBuffer!"); | |
| 71 return; | |
| 72 } | |
| 73 | |
| 74 var numberOfChannels = sourceBuffer.numberOfChannels < destBuffer.numberOfCh
annels ? sourceBuffer.numberOfChannels : destBuffer.numberOfChannels; | |
| 75 var length = numberOfChannels; | |
| 76 | |
| 77 for (var c = 0; c < numberOfChannels; ++c) { | |
| 78 var source = sourceBuffer.getChannelData(c); | |
| 79 var dest = destBuffer.getChannelData(c); | |
| 80 for (var i = 0; i < length; ++i) { | |
| 81 dest[i] += source[i]; | |
| 82 } | |
| 83 } | |
| 84 } | |
| 85 | |
| 86 // Speaker channel interpretation mixing: | |
| 87 // https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#UpMix | |
| 88 function speakersSum(sourceBuffer, destBuffer) | |
| 89 { | |
| 90 var numberOfSourceChannels = sourceBuffer.numberOfChannels; | |
| 91 var numberOfDestinationChannels = destBuffer.numberOfChannels; | |
| 92 var length = destBuffer.length; | |
| 93 | |
| 94 if (numberOfDestinationChannels == 2 && numberOfSourceChannels == 1) { | |
| 95 // Handle mono -> stereo case (summing mono channel into both left and r
ight). | |
| 96 var source = sourceBuffer.getChannelData(0); | |
| 97 var destL = destBuffer.getChannelData(0); | |
| 98 var destR = destBuffer.getChannelData(1); | |
| 99 | |
| 100 for (var i = 0; i < length; ++i) { | |
| 101 destL[i] += source[i]; | |
| 102 destR[i] += source[i]; | |
| 103 } | |
| 104 } else if (numberOfDestinationChannels == 1 && numberOfSourceChannels == 2)
{ | |
| 105 // Handle stereo -> mono case. output += 0.5 * (input.L + input.R). | |
| 106 var sourceL = sourceBuffer.getChannelData(0); | |
| 107 var sourceR = sourceBuffer.getChannelData(1); | |
| 108 var dest = destBuffer.getChannelData(0); | |
| 109 | |
| 110 for (var i = 0; i < length; ++i) { | |
| 111 dest[i] += 0.5 * (sourceL[i] + sourceR[i]); | |
| 112 } | |
| 113 } else if (numberOfDestinationChannels == 6 && numberOfSourceChannels == 1)
{ | |
| 114 // Handle mono -> 5.1 case, sum mono channel into center. | |
| 115 var source = sourceBuffer.getChannelData(0); | |
| 116 var dest = destBuffer.getChannelData(2); | |
| 117 | |
| 118 for (var i = 0; i < length; ++i) { | |
| 119 dest[i] += source[i]; | |
| 120 } | |
| 121 } else if (numberOfDestinationChannels == 1 && numberOfSourceChannels == 6)
{ | |
| 122 // Handle 5.1 -> mono. | |
| 123 var sourceL = sourceBuffer.getChannelData(0); | |
| 124 var sourceR = sourceBuffer.getChannelData(1); | |
| 125 var sourceC = sourceBuffer.getChannelData(2); | |
| 126 // skip LFE for now, according to current spec. | |
| 127 var sourceSL = sourceBuffer.getChannelData(4); | |
| 128 var sourceSR = sourceBuffer.getChannelData(5); | |
| 129 var dest = destBuffer.getChannelData(0); | |
| 130 | |
| 131 for (var i = 0; i < length; ++i) { | |
| 132 dest[i] += 0.7071 * (sourceL[i] + sourceR[i]) + sourceC[i] + 0.5 * (
sourceSL[i] + sourceSR[i]); | |
| 133 } | |
| 134 } else { | |
| 135 // Fallback for unknown combinations. | |
| 136 discreteSum(sourceBuffer, destBuffer); | |
| 137 } | |
| 138 } | 54 } |
| 139 | 55 |
| 140 function scheduleTest(testNumber, connections, channelCount, channelCountMode, c
hannelInterpretation) { | 56 function scheduleTest(testNumber, connections, channelCount, channelCountMode, c
hannelInterpretation) { |
| 141 var mixNode = context.createGain(); | 57 var mixNode = context.createGain(); |
| 142 mixNode.channelCount = channelCount; | 58 mixNode.channelCount = channelCount; |
| 143 mixNode.channelCountMode = channelCountMode; | 59 mixNode.channelCountMode = channelCountMode; |
| 144 mixNode.channelInterpretation = channelInterpretation; | 60 mixNode.channelInterpretation = channelInterpretation; |
| 145 mixNode.connect(context.destination); | 61 mixNode.connect(context.destination); |
| 146 | 62 |
| 147 for (var i = 0; i < connections.length; ++i) { | 63 for (var i = 0; i < connections.length; ++i) { |
| 148 var connectionNumberOfChannels = connections.charCodeAt(i) - "0".charCod
eAt(0); | 64 var connectionNumberOfChannels = connections.charCodeAt(i) - "0".charCod
eAt(0); |
| 149 | 65 |
| 150 var source = context.createBufferSource(); | 66 var source = context.createBufferSource(); |
| 151 // Get a buffer with the right number of channels, converting from 1-bas
ed to 0-based index. | 67 // Get a buffer with the right number of channels, converting from 1-bas
ed to 0-based index. |
| 152 var buffer = testBuffers[connectionNumberOfChannels - 1]; | 68 var buffer = testBuffers[connectionNumberOfChannels - 1]; |
| 153 source.buffer = buffer; | 69 source.buffer = buffer; |
| 154 source.connect(mixNode); | 70 source.connect(mixNode); |
| 155 | 71 |
| 156 // Start at the right offset. | 72 // Start at the right offset. |
| 157 var sampleFrameOffset = testNumber * singleTestFrameLength; | 73 var sampleFrameOffset = testNumber * singleTestFrameLength; |
| 158 var time = sampleFrameOffset / sampleRate; | 74 var time = sampleFrameOffset / sampleRate; |
| 159 source.start(time); | 75 source.start(time); |
| 160 } | 76 } |
| 161 } | 77 } |
| 162 | 78 |
| 163 function computeNumberOfChannels(connections, channelCount, channelCountMode) { | |
| 164 if (channelCountMode == "explicit") | |
| 165 return channelCount; | |
| 166 | |
| 167 var computedNumberOfChannels = 1; // Must have at least one channel. | |
| 168 | |
| 169 // Compute "computedNumberOfChannels" based on all the connections. | |
| 170 for (var i = 0; i < connections.length; ++i) { | |
| 171 var connectionNumberOfChannels = connections.charCodeAt(i) - "0".charCod
eAt(0); | |
| 172 computedNumberOfChannels = Math.max(computedNumberOfChannels, connection
NumberOfChannels); | |
| 173 } | |
| 174 | |
| 175 if (channelCountMode == "clamped-max") | |
| 176 computedNumberOfChannels = Math.min(computedNumberOfChannels, channelCou
nt); | |
| 177 | |
| 178 return computedNumberOfChannels; | |
| 179 } | |
| 180 | |
| 181 function checkTestResult(renderedBuffer, testNumber, connections, channelCount,
channelCountMode, channelInterpretation) { | 79 function checkTestResult(renderedBuffer, testNumber, connections, channelCount,
channelCountMode, channelInterpretation) { |
| 182 var s = "connections: " + connections + ", " + channelCountMode; | 80 var s = "connections: " + connections + ", " + channelCountMode; |
| 183 | 81 |
| 184 // channelCount is ignored in "max" mode. | 82 // channelCount is ignored in "max" mode. |
| 185 if (channelCountMode == "clamped-max" || channelCountMode == "explicit") { | 83 if (channelCountMode == "clamped-max" || channelCountMode == "explicit") { |
| 186 s += "(" + channelCount + ")"; | 84 s += "(" + channelCount + ")"; |
| 187 } | 85 } |
| 188 | 86 |
| 189 s += ", " + channelInterpretation; | 87 s += ", " + channelInterpretation; |
| 190 | 88 |
| 191 var computedNumberOfChannels = computeNumberOfChannels(connections, channelC
ount, channelCountMode); | 89 var computedNumberOfChannels = computeNumberOfChannels(connections, channelC
ount, channelCountMode); |
| 192 | 90 |
| 193 // Show rendered output for this test: | |
| 194 // | |
| 195 // console.log(s); | |
| 196 // var sampleFrameOffset = testNumber * singleTestFrameLength; | |
| 197 // for (var c = 0; c < renderNumberOfChannels; ++c) { | |
| 198 // var data = renderedBuffer.getChannelData(c); | |
| 199 // var s = ""; | |
| 200 // for (var sampleFrame = 0; sampleFrame < singleTestFrameLength; ++samp
leFrame) { | |
| 201 // s += data[sampleFrame + sampleFrameOffset] + " "; | |
| 202 // } | |
| 203 // s += "\n"; | |
| 204 // console.log(s); | |
| 205 // } | |
| 206 // return; | |
| 207 | |
| 208 // Create a zero-initialized silent AudioBuffer with computedNumberOfChannel
s. | 91 // Create a zero-initialized silent AudioBuffer with computedNumberOfChannel
s. |
| 209 var destBuffer = context.createBuffer(computedNumberOfChannels, singleTestFr
ameLength, context.sampleRate); | 92 var destBuffer = context.createBuffer(computedNumberOfChannels, singleTestFr
ameLength, context.sampleRate); |
| 210 | 93 |
| 211 // Mix all of the connections into the destination buffer. | 94 // Mix all of the connections into the destination buffer. |
| 212 for (var i = 0; i < connections.length; ++i) { | 95 for (var i = 0; i < connections.length; ++i) { |
| 213 var connectionNumberOfChannels = connections.charCodeAt(i) - "0".charCod
eAt(0); | 96 var connectionNumberOfChannels = connections.charCodeAt(i) - "0".charCod
eAt(0); |
| 214 var sourceBuffer = testBuffers[connectionNumberOfChannels - 1]; // conve
rt from 1-based to 0-based index | 97 var sourceBuffer = testBuffers[connectionNumberOfChannels - 1]; // conve
rt from 1-based to 0-based index |
| 215 | 98 |
| 216 if (channelInterpretation == "speakers") { | 99 if (channelInterpretation == "speakers") { |
| 217 speakersSum(sourceBuffer, destBuffer); | 100 speakersSum(sourceBuffer, destBuffer); |
| 218 } else if (channelInterpretation == "discrete") { | 101 } else if (channelInterpretation == "discrete") { |
| 219 discreteSum(sourceBuffer, destBuffer); | 102 discreteSum(sourceBuffer, destBuffer); |
| 220 } else { | 103 } else { |
| 221 alert("Invalid channel interpretation!"); | 104 alert("Invalid channel interpretation!"); |
| 222 } | 105 } |
| 223 } | 106 } |
| 224 | 107 |
| 108 // Use this when debugging mixing rules. |
| 109 // printTestInformation(testNumber, renderedBuffer, destBuffer, singleTestFr
ameLength, sampleFrameOffset); |
| 110 |
| 225 // Validate that destBuffer matches the rendered output. | 111 // Validate that destBuffer matches the rendered output. |
| 226 // We need to check the rendered output at a specific sample-frame-offset co
rresponding | 112 // We need to check the rendered output at a specific sample-frame-offset co
rresponding |
| 227 // to the specific test case we're checking for based on testNumber. | 113 // to the specific test case we're checking for based on testNumber. |
| 228 | 114 |
| 229 var sampleFrameOffset = testNumber * singleTestFrameLength; | 115 var sampleFrameOffset = testNumber * singleTestFrameLength; |
| 230 for (var c = 0; c < renderNumberOfChannels; ++c) { | 116 for (var c = 0; c < renderNumberOfChannels; ++c) { |
| 231 var renderedData = renderedBuffer.getChannelData(c); | 117 var renderedData = renderedBuffer.getChannelData(c); |
| 232 for (var frame = 0; frame < singleTestFrameLength; ++frame) { | 118 for (var frame = 0; frame < singleTestFrameLength; ++frame) { |
| 233 var renderedValue = renderedData[frame + sampleFrameOffset]; | 119 var renderedValue = renderedData[frame + sampleFrameOffset]; |
| 234 | 120 |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 286 context = new OfflineAudioContext(renderNumberOfChannels, totalFrameLength,
sampleRate); | 172 context = new OfflineAudioContext(renderNumberOfChannels, totalFrameLength,
sampleRate); |
| 287 | 173 |
| 288 // Set destination to discrete mixing. | 174 // Set destination to discrete mixing. |
| 289 context.destination.channelCount = renderNumberOfChannels; | 175 context.destination.channelCount = renderNumberOfChannels; |
| 290 context.destination.channelCountMode = "explicit"; | 176 context.destination.channelCountMode = "explicit"; |
| 291 context.destination.channelInterpretation = "discrete"; | 177 context.destination.channelInterpretation = "discrete"; |
| 292 | 178 |
| 293 // Create test buffers from 1 to 8 channels. | 179 // Create test buffers from 1 to 8 channels. |
| 294 testBuffers = new Array(); | 180 testBuffers = new Array(); |
| 295 for (var i = 0; i < renderNumberOfChannels; ++i) { | 181 for (var i = 0; i < renderNumberOfChannels; ++i) { |
| 296 testBuffers[i] = createTestBuffer(i + 1); | 182 testBuffers[i] = createShiftedImpulseBuffer(context, i + 1, singleTestFr
ameLength); |
| 297 } | 183 } |
| 298 | 184 |
| 299 // Schedule all the tests. | 185 // Schedule all the tests. |
| 300 var testNumber = 0; | 186 var testNumber = 0; |
| 301 for (var m = 0; m < mixingRulesList.length; ++m) { | 187 for (var m = 0; m < mixingRulesList.length; ++m) { |
| 302 var mixingRules = mixingRulesList[m]; | 188 var mixingRules = mixingRulesList[m]; |
| 303 for (var i = 0; i < connectionsList.length; ++i, ++testNumber) { | 189 for (var i = 0; i < connectionsList.length; ++i, ++testNumber) { |
| 304 scheduleTest(testNumber, connectionsList[i], mixingRules.channelCoun
t, mixingRules.channelCountMode, mixingRules.channelInterpretation); | 190 scheduleTest(testNumber, connectionsList[i], mixingRules.channelCoun
t, mixingRules.channelCountMode, mixingRules.channelInterpretation); |
| 305 } | 191 } |
| 306 } | 192 } |
| 307 | 193 |
| 308 // Render then check results. | 194 // Render then check results. |
| 309 context.oncomplete = checkResult; | 195 context.oncomplete = checkResult; |
| 310 context.startRendering(); | 196 context.startRendering(); |
| 311 } | 197 } |
| 312 | 198 |
| 313 runTest(); | 199 runTest(); |
| 314 | 200 |
| 315 </script> | 201 </script> |
| 316 | 202 |
| 317 </body> | 203 </body> |
| 318 </html> | 204 </html> |
| OLD | NEW |