audiobuffer-copy-channel.html (12276B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title> 5 Test Basic Functionality of AudioBuffer.copyFromChannel and 6 AudioBuffer.copyToChannel 7 </title> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script src="/webaudio/resources/audit-util.js"></script> 11 <script src="/webaudio/resources/audit.js"></script> 12 </head> 13 <body> 14 <script id="layout-test-code"> 15 // Define utility routines. 16 17 // Initialize the AudioBuffer |buffer| with a ramp signal on each channel. 18 // The ramp starts at channel number + 1. 19 function initializeAudioBufferRamp(buffer) { 20 for (let c = 0; c < buffer.numberOfChannels; ++c) { 21 let d = buffer.getChannelData(c); 22 for (let k = 0; k < d.length; ++k) { 23 d[k] = k + c + 1; 24 } 25 } 26 } 27 28 // Create a Float32Array of length |length| and initialize the array to 29 // -1. 30 function createInitializedF32Array(length) { 31 let x = new Float32Array(length); 32 for (let k = 0; k < length; ++k) { 33 x[k] = -1; 34 } 35 return x; 36 } 37 38 // Create a Float32Array of length |length| that is initialized to be a 39 // ramp starting at 1. 40 function createFloat32RampArray(length) { 41 let x = new Float32Array(length); 42 for (let k = 0; k < x.length; ++k) { 43 x[k] = k + 1; 44 } 45 46 return x; 47 } 48 49 // Test that the array |x| is a ramp starting at value |start| of length 50 // |length|, starting at |startIndex| in the array. |startIndex| is 51 // optional and defaults to 0. Any other values must be -1. 52 function shouldBeRamp( 53 should, testName, x, startValue, length, startIndex) { 54 let k; 55 let startingIndex = startIndex || 0; 56 let expected = Array(x.length); 57 58 // Fill the expected array with the correct results. 59 60 // The initial part (if any) must be -1. 61 for (k = 0; k < startingIndex; ++k) { 62 expected[k] = -1; 63 } 64 65 // The second part should be a ramp starting with |startValue| 66 for (; k < startingIndex + length; ++k) { 67 expected[k] = startValue + k - startingIndex; 68 } 69 70 // The last part (if any) should be -1. 71 for (; k < x.length; ++k) { 72 expected[k] = -1; 73 } 74 75 should(x, testName, {numberOfArrayLog: 32}).beEqualToArray(expected); 76 } 77 78 let audit = Audit.createTaskRunner(); 79 80 let context = new AudioContext(); 81 // Temp array for testing exceptions for copyToChannel/copyFromChannel. 82 // The length is arbitrary. 83 let x = new Float32Array(8); 84 85 // Number of frames in the AudioBuffer for testing. This is pretty 86 // arbitrary so choose a fairly small value. 87 let bufferLength = 16; 88 89 // Number of channels in the AudioBuffer. Also arbitrary, but it should 90 // be greater than 1 for test coverage. 91 let numberOfChannels = 3; 92 93 // AudioBuffer that will be used for testing copyFrom and copyTo. 94 let buffer = context.createBuffer( 95 numberOfChannels, bufferLength, context.sampleRate); 96 97 let initialValues = Array(numberOfChannels); 98 99 // Initialize things 100 audit.define('initialize', (task, should) => { 101 // Initialize to -1. 102 initialValues.fill(-1); 103 should(initialValues, 'Initialized values').beConstantValueOf(-1) 104 task.done(); 105 }); 106 107 // Test that expected exceptions are signaled for copyFrom. 108 audit.define('copyFrom-exceptions', (task, should) => { 109 should( 110 AudioBuffer.prototype.copyFromChannel, 111 'AudioBuffer.prototype.copyFromChannel') 112 .exist(); 113 114 should( 115 () => { 116 buffer = context.createBuffer( 117 numberOfChannels, bufferLength, context.sampleRate); 118 }, 119 '0: buffer = context.createBuffer(' + numberOfChannels + ', ' + 120 bufferLength + ', context.sampleRate)') 121 .notThrow(); 122 should(() => { 123 buffer.copyFromChannel(null, 0); 124 }, '1: buffer.copyFromChannel(null, 0)').throw(TypeError); 125 should(() => { 126 buffer.copyFromChannel(context, 0); 127 }, '2: buffer.copyFromChannel(context, 0)').throw(TypeError); 128 should(() => { 129 buffer.copyFromChannel(x, -1); 130 }, '3: buffer.copyFromChannel(x, -1)').throw(DOMException, 'IndexSizeError'); 131 should( 132 () => { 133 buffer.copyFromChannel(x, numberOfChannels); 134 }, 135 '4: buffer.copyFromChannel(x, ' + numberOfChannels + ')') 136 .throw(DOMException, 'IndexSizeError'); 137 ; 138 should(() => { 139 buffer.copyFromChannel(x, 0, -1); 140 }, '5: buffer.copyFromChannel(x, 0, -1)').notThrow(); 141 should( 142 () => { 143 buffer.copyFromChannel(x, 0, bufferLength); 144 }, 145 '6: buffer.copyFromChannel(x, 0, ' + bufferLength + ')') 146 .notThrow(); 147 148 should(() => { 149 buffer.copyFromChannel(x, 3); 150 }, '7: buffer.copyFromChannel(x, 3)').throw(DOMException, 'IndexSizeError'); 151 152 // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()` 153 // WebAssembly.Memory's size is in multiples of 64 KiB 154 const shared_buffer = new Float32Array(new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer); 155 should( 156 () => { 157 buffer.copyFromChannel(shared_buffer, 0); 158 }, 159 '8: buffer.copyFromChannel(SharedArrayBuffer view, 0)') 160 .throw(TypeError); 161 162 should( 163 () => { 164 buffer.copyFromChannel(shared_buffer, 0, 0); 165 }, 166 '9: buffer.copyFromChannel(SharedArrayBuffer view, 0, 0)') 167 .throw(TypeError); 168 169 task.done(); 170 }); 171 172 // Test that expected exceptions are signaled for copyTo. 173 audit.define('copyTo-exceptions', (task, should) => { 174 should( 175 AudioBuffer.prototype.copyToChannel, 176 'AudioBuffer.prototype.copyToChannel') 177 .exist(); 178 should(() => { 179 buffer.copyToChannel(null, 0); 180 }, '0: buffer.copyToChannel(null, 0)').throw(TypeError); 181 should(() => { 182 buffer.copyToChannel(context, 0); 183 }, '1: buffer.copyToChannel(context, 0)').throw(TypeError); 184 should(() => { 185 buffer.copyToChannel(x, -1); 186 }, '2: buffer.copyToChannel(x, -1)').throw(DOMException, 'IndexSizeError'); 187 should( 188 () => { 189 buffer.copyToChannel(x, numberOfChannels); 190 }, 191 '3: buffer.copyToChannel(x, ' + numberOfChannels + ')') 192 .throw(DOMException, 'IndexSizeError'); 193 should(() => { 194 buffer.copyToChannel(x, 0, -1); 195 }, '4: buffer.copyToChannel(x, 0, -1)').notThrow(); 196 should( 197 () => { 198 buffer.copyToChannel(x, 0, bufferLength); 199 }, 200 '5: buffer.copyToChannel(x, 0, ' + bufferLength + ')') 201 .notThrow(); 202 203 should(() => { 204 buffer.copyToChannel(x, 3); 205 }, '6: buffer.copyToChannel(x, 3)').throw(DOMException, 'IndexSizeError'); 206 207 // See https://github.com/whatwg/html/issues/5380 for why not `new SharedArrayBuffer()` 208 // WebAssembly.Memory's size is in multiples of 64 KiB 209 const shared_buffer = new Float32Array(new WebAssembly.Memory({ shared:true, initial:1, maximum:1 }).buffer); 210 should( 211 () => { 212 buffer.copyToChannel(shared_buffer, 0); 213 }, 214 '7: buffer.copyToChannel(SharedArrayBuffer view, 0)') 215 .throw(TypeError); 216 217 should( 218 () => { 219 buffer.copyToChannel(shared_buffer, 0, 0); 220 }, 221 '8: buffer.copyToChannel(SharedArrayBuffer view, 0, 0)') 222 .throw(TypeError); 223 224 task.done(); 225 }); 226 227 // Test copyFromChannel 228 audit.define('copyFrom-validate', (task, should) => { 229 // Initialize the AudioBuffer to a ramp for testing copyFrom. 230 initializeAudioBufferRamp(buffer); 231 232 // Test copyFrom operation with a short destination array, filling the 233 // destination completely. 234 for (let c = 0; c < numberOfChannels; ++c) { 235 let dst8 = createInitializedF32Array(8); 236 buffer.copyFromChannel(dst8, c); 237 shouldBeRamp( 238 should, 'buffer.copyFromChannel(dst8, ' + c + ')', dst8, c + 1, 8) 239 } 240 241 // Test copyFrom operation with a short destination array using a 242 // non-zero start index that still fills the destination completely. 243 for (let c = 0; c < numberOfChannels; ++c) { 244 let dst8 = createInitializedF32Array(8); 245 buffer.copyFromChannel(dst8, c, 1); 246 shouldBeRamp( 247 should, 'buffer.copyFromChannel(dst8, ' + c + ', 1)', dst8, c + 2, 248 8) 249 } 250 251 // Test copyFrom operation with a short destination array using a 252 // non-zero start index that does not fill the destinatiom completely. 253 // The extra elements should be unchanged. 254 for (let c = 0; c < numberOfChannels; ++c) { 255 let dst8 = createInitializedF32Array(8); 256 let startInChannel = bufferLength - 5; 257 buffer.copyFromChannel(dst8, c, startInChannel); 258 shouldBeRamp( 259 should, 260 'buffer.copyFromChannel(dst8, ' + c + ', ' + startInChannel + ')', 261 dst8, c + 1 + startInChannel, bufferLength - startInChannel); 262 } 263 264 // Copy operation with the destination longer than the buffer, leaving 265 // the trailing elements of the destination untouched. 266 for (let c = 0; c < numberOfChannels; ++c) { 267 let dst26 = createInitializedF32Array(bufferLength + 10); 268 buffer.copyFromChannel(dst26, c); 269 shouldBeRamp( 270 should, 'buffer.copyFromChannel(dst26, ' + c + ')', dst26, c + 1, 271 bufferLength); 272 } 273 274 task.done(); 275 }); 276 277 // Test copyTo 278 audit.define('copyTo-validate', (task, should) => { 279 // Create a source consisting of a ramp starting at 1, longer than the 280 // AudioBuffer 281 let src = createFloat32RampArray(bufferLength + 10); 282 283 // Test copyTo with AudioBuffer shorter than Float32Array. The 284 // AudioBuffer should be completely filled with the Float32Array. 285 should( 286 () => { 287 buffer = 288 createConstantBuffer(context, bufferLength, initialValues); 289 }, 290 'buffer = createConstantBuffer(context, ' + bufferLength + ', [' + 291 initialValues + '])') 292 .notThrow(); 293 294 for (let c = 0; c < numberOfChannels; ++c) { 295 buffer.copyToChannel(src, c); 296 shouldBeRamp( 297 should, 'buffer.copyToChannel(src, ' + c + ')', 298 buffer.getChannelData(c), 1, bufferLength); 299 } 300 301 // Test copyTo with AudioBuffer longer than the Float32Array. The tail 302 // of the AudioBuffer should be unchanged. 303 buffer = createConstantBuffer(context, bufferLength, initialValues); 304 let src10 = createFloat32RampArray(10); 305 for (let c = 0; c < numberOfChannels; ++c) { 306 buffer.copyToChannel(src10, c); 307 shouldBeRamp( 308 should, 'buffer.copyToChannel(src10, ' + c + ')', 309 buffer.getChannelData(c), 1, 10); 310 } 311 312 // Test copyTo with non-default startInChannel. Part of the AudioBuffer 313 // should filled with the beginning and end sections untouched. 314 buffer = createConstantBuffer(context, bufferLength, initialValues); 315 for (let c = 0; c < numberOfChannels; ++c) { 316 let startInChannel = 5; 317 buffer.copyToChannel(src10, c, startInChannel); 318 319 shouldBeRamp( 320 should, 321 'buffer.copyToChannel(src10, ' + c + ', ' + startInChannel + ')', 322 buffer.getChannelData(c), 1, src10.length, startInChannel); 323 } 324 task.done(); 325 }); 326 327 audit.run(); 328 </script> 329 </body> 330 </html>