| 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 audiobuffersource-loop-comprehensive.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/audiobuffersource-testing.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/audiobuffersource-testing.js"></script> |
| 13 <script> | 12 </head> |
| 14 let audit = Audit.createTaskRunner(); | 13 <body> |
| 15 | 14 <script id="layout-test-code"> |
| 16 // The following test cases assume an AudioBuffer of length 8 whose PCM data is
a linear ramp, 0, 1, 2, 3,... | 15 let audit = Audit.createTaskRunner(); |
| 17 // |description| is optional and will be computed from the other parameters. |of
fsetFrame| is | 16 |
| 18 // optional and defaults to 0. | 17 // The following test cases assume an AudioBuffer of length 8 whose PCM |
| 19 | 18 // data is a linear ramp, 0, 1, 2, 3,... |description| is optional and |
| 20 let tests = [ | 19 // will be computed from the other parameters. |offsetFrame| is optional |
| 21 | 20 // and defaults to 0. |
| 22 { description: "loop whole buffer by default with loopStart == loopEnd == 0", | 21 |
| 23 loopStartFrame: 0, | 22 let tests = [ |
| 24 loopEndFrame: 0, | 23 |
| 25 renderFrames: 16, | 24 { |
| 26 playbackRate: 1, | 25 description: |
| 27 expected: [0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7] }, | 26 'loop whole buffer by default with loopStart == loopEnd == 0', |
| 28 | 27 loopStartFrame: 0, |
| 29 { description: "loop whole buffer explicitly", | 28 loopEndFrame: 0, |
| 30 loopStartFrame: 0, | 29 renderFrames: 16, |
| 31 loopEndFrame: 8, | 30 playbackRate: 1, |
| 32 renderFrames: 16, | 31 expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7] |
| 33 playbackRate: 1, | 32 }, |
| 34 expected: [0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7] }, | 33 |
| 35 | 34 { |
| 36 { description: "loop from middle to end of buffer", | 35 description: 'loop whole buffer explicitly', |
| 37 loopStartFrame: 4, | 36 loopStartFrame: 0, |
| 38 loopEndFrame: 8, | 37 loopEndFrame: 8, |
| 39 renderFrames: 16, | 38 renderFrames: 16, |
| 40 playbackRate: 1, | 39 playbackRate: 1, |
| 41 expected: [0,1,2,3,4,5,6,7,4,5,6,7,4,5,6,7] }, | 40 expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7] |
| 42 | 41 }, |
| 43 { description: "loop from start to middle of buffer", | 42 |
| 44 loopStartFrame: 0, | 43 { |
| 45 loopEndFrame: 4, | 44 description: 'loop from middle to end of buffer', |
| 46 renderFrames: 16, | 45 loopStartFrame: 4, |
| 47 playbackRate: 1, | 46 loopEndFrame: 8, |
| 48 expected: [0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3] }, | 47 renderFrames: 16, |
| 49 | 48 playbackRate: 1, |
| 50 { loopStartFrame: 4, | 49 expected: [0, 1, 2, 3, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7] |
| 51 loopEndFrame: 6, | 50 }, |
| 52 renderFrames: 16, | 51 |
| 53 playbackRate: 1, | 52 { |
| 54 expected: [0,1,2,3,4,5,4,5,4,5,4,5,4,5,4,5] }, | 53 description: 'loop from start to middle of buffer', |
| 55 | 54 loopStartFrame: 0, |
| 56 { loopStartFrame: 3, | 55 loopEndFrame: 4, |
| 57 loopEndFrame: 7, | 56 renderFrames: 16, |
| 58 renderFrames: 16, | 57 playbackRate: 1, |
| 59 playbackRate: 1, | 58 expected: [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3] |
| 60 expected: [0,1,2,3,4,5,6,3,4,5,6,3,4,5,6,3] }, | 59 }, |
| 61 | 60 |
| 62 { loopStartFrame: 4, | 61 { |
| 63 loopEndFrame: 6, | 62 loopStartFrame: 4, |
| 64 renderFrames: 16, | 63 loopEndFrame: 6, |
| 65 playbackRate: 0.5, | 64 renderFrames: 16, |
| 66 expected: [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 4, 4.5, 5, 5.5] }, | 65 playbackRate: 1, |
| 67 | 66 expected: [0, 1, 2, 3, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5] |
| 68 { loopStartFrame: 4, | 67 }, |
| 69 loopEndFrame: 6, | 68 |
| 70 renderFrames: 16, | 69 { |
| 71 playbackRate: 1.5, | 70 loopStartFrame: 3, |
| 72 expected: [0, 1.5, 3, 4.5, 4, 5.5, 5, 4.5, 4, 5.5, 5, 4.5, 4, 5.5, 5, 4.5] }, | 71 loopEndFrame: 7, |
| 73 | 72 renderFrames: 16, |
| 74 // Offset past loop end, so playback starts at loop start | 73 playbackRate: 1, |
| 75 { loopStartFrame: 2, | 74 expected: [0, 1, 2, 3, 4, 5, 6, 3, 4, 5, 6, 3, 4, 5, 6, 3] |
| 76 loopEndFrame: 5, | 75 }, |
| 77 renderFrames: 16, | 76 |
| 78 playbackRate: 1, | 77 { |
| 79 offsetFrame: 6, | 78 loopStartFrame: 4, |
| 80 expected: [2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2] }, | 79 loopEndFrame: 6, |
| 81 | 80 renderFrames: 16, |
| 82 // Offset before loop start, so start at offset and continue | 81 playbackRate: 0.5, |
| 83 { loopStartFrame: 3, | 82 expected: |
| 84 loopEndFrame: 6, | 83 [0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 4, 4.5, 5, 5.5] |
| 85 renderFrames: 16, | 84 }, |
| 86 playbackRate: 1, | 85 |
| 87 offsetFrame: 1, | 86 { |
| 88 expected: [1, 2, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4] }, | 87 loopStartFrame: 4, |
| 89 | 88 loopEndFrame: 6, |
| 90 // Offset between loop start and loop end, so start at offset and continue | 89 renderFrames: 16, |
| 91 { loopStartFrame: 3, | 90 playbackRate: 1.5, |
| 92 loopEndFrame: 6, | 91 expected: |
| 93 renderFrames: 16, | 92 [0, 1.5, 3, 4.5, 4, 5.5, 5, 4.5, 4, 5.5, 5, 4.5, 4, 5.5, 5, 4.5] |
| 94 playbackRate: 1, | 93 }, |
| 95 offsetFrame: 4, | 94 |
| 96 expected: [4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4] }, | 95 // Offset past loop end, so playback starts at loop start |
| 97 | 96 { |
| 98 { description: "illegal playbackRate of 47 greater than loop length", | 97 loopStartFrame: 2, |
| 99 loopStartFrame: 4, | 98 loopEndFrame: 5, |
| 100 loopEndFrame: 6, | 99 renderFrames: 16, |
| 101 renderFrames: 16, | 100 playbackRate: 1, |
| 102 playbackRate: 47, | 101 offsetFrame: 6, |
| 103 expected: [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] }, | 102 expected: [2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2, 3, 4, 2] |
| 104 | 103 }, |
| 105 // Try illegal loop-points - they should be ignored and we'll loop the whole buf
fer. | 104 |
| 106 | 105 // Offset before loop start, so start at offset and continue |
| 107 { description: "illegal loop: loopStartFrame > loopEndFrame", | 106 { |
| 108 loopStartFrame: 7, | 107 loopStartFrame: 3, |
| 109 loopEndFrame: 3, | 108 loopEndFrame: 6, |
| 110 renderFrames: 16, | 109 renderFrames: 16, |
| 111 playbackRate: 1, | 110 playbackRate: 1, |
| 112 expected: [0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7] }, | 111 offsetFrame: 1, |
| 113 | 112 expected: [1, 2, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4] |
| 114 { description: "illegal loop: loopStartFrame == loopEndFrame", | 113 }, |
| 115 loopStartFrame: 3, | 114 |
| 116 loopEndFrame: 3, | 115 // Offset between loop start and loop end, so start at offset and |
| 117 renderFrames: 16, | 116 // continue |
| 118 playbackRate: 1, | 117 { |
| 119 expected: [0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7] }, | 118 loopStartFrame: 3, |
| 120 | 119 loopEndFrame: 6, |
| 121 { description: "illegal loop: loopStartFrame < 0", | 120 renderFrames: 16, |
| 122 loopStartFrame: -8, | 121 playbackRate: 1, |
| 123 loopEndFrame: 3, | 122 offsetFrame: 4, |
| 124 renderFrames: 16, | 123 expected: [4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4] |
| 125 playbackRate: 1, | 124 }, |
| 126 expected: [0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7] }, | 125 |
| 127 | 126 { |
| 128 { description: "illegal loop: loopEndFrame > bufferLength", | 127 description: 'illegal playbackRate of 47 greater than loop length', |
| 129 loopStartFrame: 0, | 128 loopStartFrame: 4, |
| 130 loopEndFrame: 30000, | 129 loopEndFrame: 6, |
| 131 renderFrames: 16, | 130 renderFrames: 16, |
| 132 playbackRate: 1, | 131 playbackRate: 47, |
| 133 expected: [0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7] }, | 132 expected: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] |
| 134 | 133 }, |
| 135 // Start a loop with a duration longer than the buffer. The output should be th
e data from frame 1 | 134 |
| 136 // to 6, and then looping from 3 to 5 until 20 frames have been played. | 135 // Try illegal loop-points - they should be ignored and we'll loop the |
| 137 { description: "loop from 3 -> 6 with offset 1 for 20 frames", | 136 // whole buffer. |
| 138 loopStartFrame: 3, | 137 |
| 139 loopEndFrame: 6, | 138 { |
| 140 playbackRate: 1, | 139 description: 'illegal loop: loopStartFrame > loopEndFrame', |
| 141 offsetFrame: 1, | 140 loopStartFrame: 7, |
| 142 renderFrames: 30, | 141 loopEndFrame: 3, |
| 143 durationFrames: 20, | 142 renderFrames: 16, |
| 144 expected: [1, 2, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 0, 0, 0
, 0, 0, 0, 0, 0, 0, 0] }, | 143 playbackRate: 1, |
| 145 | 144 expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7] |
| 146 // Start a loop with a duration less than the length of the looping frames. The
output should be | 145 }, |
| 147 // the data from frame 1 to 3, and then stopping because duration = 3 | 146 |
| 148 { description: "loop from 3 -> 8 with offset 1 for 3 frames", | 147 { |
| 149 loopStartFrame: 3, | 148 description: 'illegal loop: loopStartFrame == loopEndFrame', |
| 150 loopEndFrame: 8, | 149 loopStartFrame: 3, |
| 151 playbackRate: 1, | 150 loopEndFrame: 3, |
| 152 offsetFrame: 1, | 151 renderFrames: 16, |
| 153 durationFrames: 3, | 152 playbackRate: 1, |
| 154 renderFrames: 30, | 153 expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7] |
| 155 expected: [1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, 0] }, | 154 }, |
| 156 | 155 |
| 157 // Start a loop with a duration less than the length of the looping frames. The
output should be | 156 { |
| 158 // the data from frame 1 to 3, and then stopping because duration = 3 | 157 description: 'illegal loop: loopStartFrame < 0', |
| 159 { description: "loop from 3 -> 8 with offset 7 for 3 frames", | 158 loopStartFrame: -8, |
| 160 loopStartFrame: 3, | 159 loopEndFrame: 3, |
| 161 loopEndFrame: 8, | 160 renderFrames: 16, |
| 162 playbackRate: 1, | 161 playbackRate: 1, |
| 163 offsetFrame: 7, | 162 expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7] |
| 164 durationFrames: 3, | 163 }, |
| 165 renderFrames: 30, | 164 |
| 166 expected: [7, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, 0] } | 165 { |
| 167 | 166 description: 'illegal loop: loopEndFrame > bufferLength', |
| 168 ]; | 167 loopStartFrame: 0, |
| 169 | 168 loopEndFrame: 30000, |
| 170 let sampleRate = 44100; | 169 renderFrames: 16, |
| 171 let buffer; | 170 playbackRate: 1, |
| 172 let bufferFrameLength = 8; | 171 expected: [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7] |
| 173 let testSpacingFrames = 32; | 172 }, |
| 174 let testSpacingSeconds = testSpacingFrames / sampleRate; | 173 |
| 175 let totalRenderLengthFrames = tests.length * testSpacingFrames; | 174 // Start a loop with a duration longer than the buffer. The output |
| 176 | 175 // should be the data from frame 1 to 6, and then looping from 3 to 5 |
| 177 function runLoopTest(context, testNumber, test, should) { | 176 // until 20 frames have been played. |
| 178 let source = context.createBufferSource(); | 177 { |
| 179 | 178 description: 'loop from 3 -> 6 with offset 1 for 20 frames', |
| 180 source.buffer = buffer; | 179 loopStartFrame: 3, |
| 181 source.playbackRate.value = test.playbackRate; | 180 loopEndFrame: 6, |
| 182 source.loop = true; | 181 playbackRate: 1, |
| 183 source.loopStart = test.loopStartFrame / context.sampleRate; | 182 offsetFrame: 1, |
| 184 source.loopEnd = test.loopEndFrame / context.sampleRate; | 183 renderFrames: 30, |
| 185 | 184 durationFrames: 20, |
| 186 let offset = test.offsetFrame ? test.offsetFrame / context.sampleRate : 0; | 185 expected: [ |
| 187 | 186 1, 2, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, 4, 5, 3, |
| 188 source.connect(context.destination); | 187 4, 5, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
| 189 | 188 ] |
| 190 // Render each test one after the other, spaced apart by testSpacingSeconds. | 189 }, |
| 191 let startTime = testNumber * testSpacingSeconds; | 190 |
| 192 | 191 // Start a loop with a duration less than the length of the looping |
| 193 // If durationFrames is given, run the test for the specified duration. | 192 // frames. The output should be the data from frame 1 to 3, and then |
| 194 if (test.durationFrames) { | 193 // stopping because duration = 3 |
| 195 if (!test.renderFrames) { | 194 { |
| 196 throw("renderFrames is required for test " + testNumber + ": " + tes
t.description); | 195 description: 'loop from 3 -> 8 with offset 1 for 3 frames', |
| 196 loopStartFrame: 3, |
| 197 loopEndFrame: 8, |
| 198 playbackRate: 1, |
| 199 offsetFrame: 1, |
| 200 durationFrames: 3, |
| 201 renderFrames: 30, |
| 202 expected: [ |
| 203 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 204 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
| 205 ] |
| 206 }, |
| 207 |
| 208 // Start a loop with a duration less than the length of the looping |
| 209 // frames. The output should be the data from frame 1 to 3, and then |
| 210 // stopping because duration = 3 |
| 211 { |
| 212 description: 'loop from 3 -> 8 with offset 7 for 3 frames', |
| 213 loopStartFrame: 3, |
| 214 loopEndFrame: 8, |
| 215 playbackRate: 1, |
| 216 offsetFrame: 7, |
| 217 durationFrames: 3, |
| 218 renderFrames: 30, |
| 219 expected: [ |
| 220 7, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 221 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
| 222 ] |
| 223 } |
| 224 |
| 225 ]; |
| 226 |
| 227 let sampleRate = 44100; |
| 228 let buffer; |
| 229 let bufferFrameLength = 8; |
| 230 let testSpacingFrames = 32; |
| 231 let testSpacingSeconds = testSpacingFrames / sampleRate; |
| 232 let totalRenderLengthFrames = tests.length * testSpacingFrames; |
| 233 |
| 234 function runLoopTest(context, testNumber, test, should) { |
| 235 let source = context.createBufferSource(); |
| 236 |
| 237 source.buffer = buffer; |
| 238 source.playbackRate.value = test.playbackRate; |
| 239 source.loop = true; |
| 240 source.loopStart = test.loopStartFrame / context.sampleRate; |
| 241 source.loopEnd = test.loopEndFrame / context.sampleRate; |
| 242 |
| 243 let offset = |
| 244 test.offsetFrame ? test.offsetFrame / context.sampleRate : 0; |
| 245 |
| 246 source.connect(context.destination); |
| 247 |
| 248 // Render each test one after the other, spaced apart by |
| 249 // testSpacingSeconds. |
| 250 let startTime = testNumber * testSpacingSeconds; |
| 251 |
| 252 // If durationFrames is given, run the test for the specified duration. |
| 253 if (test.durationFrames) { |
| 254 if (!test.renderFrames) { |
| 255 throw( |
| 256 'renderFrames is required for test ' + testNumber + ': ' + |
| 257 test.description); |
| 258 } else { |
| 259 if (test.durationFrames > testSpacingFrames || |
| 260 test.durationFrames < 0) { |
| 261 throw( |
| 262 'Test ' + testNumber + ': durationFrames (' + |
| 263 test.durationFrames + ') outside the range [0, ' + |
| 264 testSpacingFrames + ']'); |
| 265 } |
| 266 source.start( |
| 267 startTime, offset, test.durationFrames / context.sampleRate); |
| 268 } |
| 269 } else if (test.renderFrames) { |
| 270 let duration = test.renderFrames / context.sampleRate; |
| 271 if (test.renderFrames > testSpacingFrames || test.renderFrames < 0) { |
| 272 throw( |
| 273 'Test ' + testNumber + ': renderFrames (' + test.renderFrames + |
| 274 ') outside the range [0, ' + testSpacingFrames + ']'); |
| 275 } |
| 276 source.start(startTime, offset); |
| 277 source.stop(startTime + duration); |
| 197 } else { | 278 } else { |
| 198 if (test.durationFrames > testSpacingFrames || test.durationFrames <
0) { | 279 throw( |
| 199 throw("Test " + testNumber | 280 'Test ' + testNumber + |
| 200 + ": durationFrames (" + test.durationFrames + ") outside th
e range [0, " | 281 ' must specify renderFrames and possibly durationFrames'); |
| 201 + testSpacingFrames + "]"); | |
| 202 } | |
| 203 source.start(startTime, offset, test.durationFrames / context.sample
Rate); | |
| 204 } | 282 } |
| 205 } else if (test.renderFrames) { | 283 } |
| 206 let duration = test.renderFrames / context.sampleRate; | 284 |
| 207 if (test.renderFrames > testSpacingFrames || test.renderFrames < 0) { | 285 audit.define('AudioBufferSource looping test', function(task, should) { |
| 208 throw("Test " + testNumber | 286 // Create offline audio context. |
| 209 + ": renderFrames (" + test.renderFrames + ") outside the range
[0, " | 287 let context = |
| 210 + testSpacingFrames + "]"); | 288 new OfflineAudioContext(1, totalRenderLengthFrames, sampleRate); |
| 211 } | 289 buffer = createTestBuffer(context, bufferFrameLength); |
| 212 source.start(startTime, offset); | 290 |
| 213 source.stop(startTime + duration); | 291 should(function() { |
| 214 } else { | 292 for (let i = 0; i < tests.length; ++i) |
| 215 throw("Test " + testNumber + " must specify renderFrames and possibly du
rationFrames"); | 293 runLoopTest(context, i, tests[i], should); |
| 216 } | 294 }, 'Generate ' + tests.length + ' test cases').notThrow(); |
| 217 } | 295 |
| 218 | 296 context.startRendering().then(function(audioBuffer) { |
| 219 audit.define("AudioBufferSource looping test", function (task, should) { | 297 checkAllTests(audioBuffer, should); |
| 220 // Create offline audio context. | 298 task.done(); |
| 221 let context = new OfflineAudioContext(1, totalRenderLengthFrames, sampleRate
); | 299 }); |
| 222 buffer = createTestBuffer(context, bufferFrameLength); | |
| 223 | |
| 224 should(function () { | |
| 225 for (let i = 0; i < tests.length; ++i) | |
| 226 runLoopTest(context, i, tests[i], should); | |
| 227 }, "Generate " + tests.length + " test cases").notThrow(); | |
| 228 | |
| 229 context.startRendering() | |
| 230 .then(function (audioBuffer) { | |
| 231 checkAllTests(audioBuffer, should); | |
| 232 task.done(); | |
| 233 }); | 300 }); |
| 234 }); | 301 |
| 235 | 302 audit.run(); |
| 236 audit.run(); | 303 </script> |
| 237 </script> | 304 </body> |
| 238 | |
| 239 </body> | |
| 240 </html> | 305 </html> |
| OLD | NEW |