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 |