| OLD | NEW |
| 1 <!doctype html> | 1 <!doctype html> |
| 2 <html> | 2 <html> |
| 3 <head> | 3 <head> |
| 4 <title>Test Interpolation for AudioParam.setValueCurveAtTime</title> | 4 <title>Test Interpolation for AudioParam.setValueCurveAtTime</title> |
| 5 <script src="../../resources/testharness.js"></script> | 5 <script src="../../resources/testharness.js"></script> |
| 6 <script src="../../resources/testharnessreport.js"></script> | 6 <script src="../../resources/testharnessreport.js"></script> |
| 7 <script src="../resources/audit-util.js"></script> | 7 <script src="../resources/audit-util.js"></script> |
| 8 <script src="../resources/audio-testing.js"></script> | 8 <script src="../resources/audit.js"></script> |
| 9 <title>Test Interpolation for AudioParam.setValueCurveAtTime</title> | 9 <title>Test Interpolation for AudioParam.setValueCurveAtTime</title> |
| 10 </head> | 10 </head> |
| 11 | 11 |
| 12 <body> | 12 <body> |
| 13 <script> | 13 <script> |
| 14 | 14 |
| 15 // Play a constant signal through a gain node that is automated using setV
alueCurveAtTime with | 15 // Play a constant signal through a gain node that is automated using setV
alueCurveAtTime with |
| 16 // a 2-element curve. The output should be a linear change. | 16 // a 2-element curve. The output should be a linear change. |
| 17 | 17 |
| 18 // Choose a sample rate that is a multiple of 128, the rendering quantum s
ize. This makes the | 18 // Choose a sample rate that is a multiple of 128, the rendering quantum s
ize. This makes the |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 105 curveStartTime: 256 / sampleRate, | 105 curveStartTime: 256 / sampleRate, |
| 106 curveDuration: .5, | 106 curveDuration: .5, |
| 107 curveLength: 1000, | 107 curveLength: 1000, |
| 108 fullGainTime: 0.75, | 108 fullGainTime: 0.75, |
| 109 maxErrorThreshold: 5.9605e-8, | 109 maxErrorThreshold: 5.9605e-8, |
| 110 snrThreshold: 152.784 | 110 snrThreshold: 152.784 |
| 111 }]; | 111 }]; |
| 112 | 112 |
| 113 // Creates a function based on the test config that is suitable for use by
defineTask(). | 113 // Creates a function based on the test config that is suitable for use by
defineTask(). |
| 114 function createTaskFunction(config) { | 114 function createTaskFunction(config) { |
| 115 return function (done) { | 115 return function (task, should) { |
| 116 runTest(config).then(done); | 116 runTest(should, config).then(() => task.done()); |
| 117 }; | 117 }; |
| 118 } | 118 } |
| 119 | 119 |
| 120 // Define a task for each config, in the order listed in testConfigs. | 120 // Define a task for each config, in the order listed in testConfigs. |
| 121 for (var k = 0; k < testConfigs.length; ++k) { | 121 for (var k = 0; k < testConfigs.length; ++k) { |
| 122 var config = testConfigs[k]; | 122 var config = testConfigs[k]; |
| 123 var name = k + ":curve=" + config.curveLength + ",duration=" + (config.c
urveDuration * sampleRate); | 123 var name = k + ":curve=" + config.curveLength + ",duration=" + (config.c
urveDuration * sampleRate); |
| 124 audit.defineTask(name, createTaskFunction(config)); | 124 audit.define(name, createTaskFunction(config)); |
| 125 } | 125 } |
| 126 | 126 |
| 127 // Simple test from crbug.com/441471. Makes sure the end points and the m
iddle point are | 127 // Simple test from crbug.com/441471. Makes sure the end points and the m
iddle point are |
| 128 // interpolated correctly. | 128 // interpolated correctly. |
| 129 audit.defineTask("crbug-441471", function (done) { | 129 audit.define("crbug-441471", (task, should) => { |
| 130 // Any sample rate should work; we pick something small such that the ti
me end points are on | 130 // Any sample rate should work; we pick something small such that the ti
me end points are on |
| 131 // a sampling point. | 131 // a sampling point. |
| 132 var context = new OfflineAudioContext(1, 5000, 5000) | 132 var context = new OfflineAudioContext(1, 5000, 5000) |
| 133 | 133 |
| 134 // A constant source | 134 // A constant source |
| 135 var source = context.createBufferSource(); | 135 var source = context.createBufferSource(); |
| 136 source.buffer = createConstantBuffer(context, 1, 1); | 136 source.buffer = createConstantBuffer(context, 1, 1); |
| 137 source.loop = true; | 137 source.loop = true; |
| 138 | 138 |
| 139 var gain = context.createGain(); | 139 var gain = context.createGain(); |
| (...skipping 10 matching lines...) Expand all Loading... |
| 150 c[2] = 1; | 150 c[2] = 1; |
| 151 gain.gain.setValueCurveAtTime(c, startTime, duration); | 151 gain.gain.setValueCurveAtTime(c, startTime, duration); |
| 152 source.connect(gain); | 152 source.connect(gain); |
| 153 gain.connect(context.destination); | 153 gain.connect(context.destination); |
| 154 source.start(); | 154 source.start(); |
| 155 | 155 |
| 156 context.startRendering().then(function (renderedBuffer) { | 156 context.startRendering().then(function (renderedBuffer) { |
| 157 var data = renderedBuffer.getChannelData(0); | 157 var data = renderedBuffer.getChannelData(0); |
| 158 var endTime = startTime + duration; | 158 var endTime = startTime + duration; |
| 159 var midPoint = (startTime + endTime) / 2; | 159 var midPoint = (startTime + endTime) / 2; |
| 160 var success = true; | |
| 161 | 160 |
| 162 success = Should("Curve value at time " + startTime, | 161 should(data[timeToSampleFrame(startTime, context.sampleRate)], |
| 163 data[timeToSampleFrame(startTime, context.sampleRate)]).beEqualTo(c[
0]) && success; | 162 "Curve value at time " + startTime) |
| 163 .beEqualTo(c[0]); |
| 164 // Due to round-off, the value at the midpoint is not exactly zero on
arm64. See | 164 // Due to round-off, the value at the midpoint is not exactly zero on
arm64. See |
| 165 // crbug.com/558563. The current value is experimentally determined. | 165 // crbug.com/558563. The current value is experimentally determined. |
| 166 success = Should("Curve value at time " + midPoint, | 166 should(data[timeToSampleFrame(midPoint, context.sampleRate)], |
| 167 data[timeToSampleFrame(midPoint, context.sampleRate)]).beCloseTo(0,
Math.pow(2, -51)) && success; | 167 "Curve value at time " + midPoint) |
| 168 success = Should("Curve value at time " + endTime, | 168 .beCloseTo(0, {threshold: Math.pow(2, -51)}); |
| 169 data[timeToSampleFrame(endTime, context.sampleRate)]).beEqualTo(c[2]
) && success; | 169 should(data[timeToSampleFrame(endTime, context.sampleRate)], |
| 170 Should("Test: crbug.com/44471", success) | 170 "Curve value at time " + endTime) |
| 171 .summarize("passed", "failed"); | 171 .beEqualTo(c[2]); |
| 172 }).then(done); | 172 }).then(() => task.done()); |
| 173 }); | 173 }); |
| 174 | 174 |
| 175 // Must be the last defined task. | 175 function runTest(should, config) { |
| 176 audit.defineTask("end", function (done) { | |
| 177 done(); | |
| 178 }); | |
| 179 | |
| 180 function runTest(config) { | |
| 181 context = new OfflineAudioContext(1, testDurationFrames, sampleRate); | 176 context = new OfflineAudioContext(1, testDurationFrames, sampleRate); |
| 182 | 177 |
| 183 // A constant audio source of value 1. | 178 // A constant audio source of value 1. |
| 184 var source = context.createBufferSource(); | 179 var source = context.createBufferSource(); |
| 185 source.buffer = createConstantBuffer(context, 1, 1); | 180 source.buffer = createConstantBuffer(context, 1, 1); |
| 186 source.loop = true; | 181 source.loop = true; |
| 187 | 182 |
| 188 // The value curve for testing. Just to make things easy for testing, m
ake the curve a | 183 // The value curve for testing. Just to make things easy for testing, m
ake the curve a |
| 189 // simple ramp up to curveValue. | 184 // simple ramp up to curveValue. |
| 190 // TODO(rtoy): Maybe allow more complicated curves? | 185 // TODO(rtoy): Maybe allow more complicated curves? |
| 191 var curve = new Float32Array(config.curveLength); | 186 var curve = new Float32Array(config.curveLength); |
| 192 for (var k = 0; k < config.curveLength; ++k) { | 187 for (var k = 0; k < config.curveLength; ++k) { |
| 193 curve[k] = curveValue / (config.curveLength - 1) * k; | 188 curve[k] = curveValue / (config.curveLength - 1) * k; |
| 194 } | 189 } |
| 195 | 190 |
| 196 // A gain node that is to be automated using setValueCurveAtTime. | 191 // A gain node that is to be automated using setValueCurveAtTime. |
| 197 var gain = context.createGain(); | 192 var gain = context.createGain(); |
| 198 gain.gain.value = 0; | 193 gain.gain.value = 0; |
| 199 gain.gain.setValueCurveAtTime(curve, config.curveStartTime, config.curve
Duration); | 194 gain.gain.setValueCurveAtTime(curve, config.curveStartTime, config.curve
Duration); |
| 200 // This is to verify that setValueCurveAtTime ends appropriately. | 195 // This is to verify that setValueCurveAtTime ends appropriately. |
| 201 gain.gain.setValueAtTime(1, config.fullGainTime); | 196 gain.gain.setValueAtTime(1, config.fullGainTime); |
| 202 | 197 |
| 203 source.connect(gain); | 198 source.connect(gain); |
| 204 gain.connect(context.destination); | 199 gain.connect(context.destination); |
| 205 source.start(); | 200 source.start(); |
| 206 | 201 |
| 207 // Some consistency checks on the test parameters | 202 // Some consistency checks on the test parameters |
| 208 let prefix = "Length " + config.curveLength + ", duration " + config.cur
veDuration; | 203 let prefix = "Length " + config.curveLength + ", duration " + config.cur
veDuration; |
| 209 Should(prefix + ": Check: Curve end time", config.curveStartTime + confi
g.curveDuration, { | 204 should(config.curveStartTime + config.curveDuration, |
| 210 brief: true | 205 prefix + ": Check: Curve end time") |
| 211 }) | |
| 212 .beLessThanOrEqualTo(testDurationSec); | 206 .beLessThanOrEqualTo(testDurationSec); |
| 213 Should(prefix + ": Check: Full gain start time", config.fullGainTime, { | 207 should(config.fullGainTime, prefix + ": Check: Full gain start time") |
| 214 brief: true | 208 .beLessThanOrEqualTo(testDurationSec); |
| 215 }).beLessThanOrEqualTo(testDurationSec); | 209 should(config.fullGainTime, |
| 216 Should(prefix + ": Check: Full gain start time", config.fullGainTime, { | 210 prefix + ": Check: Full gain start time") |
| 217 brief: true | 211 .beGreaterThanOrEqualTo(config.curveStartTime + config.curveDuration); |
| 218 }).beGreaterThanOrEqualTo(config.curveStartTime + config.curveDuration); | |
| 219 | 212 |
| 220 // Rock and roll! | 213 // Rock and roll! |
| 221 return context.startRendering().then(checkResult(config)); | 214 return context.startRendering().then(checkResult(should, config)); |
| 222 } | 215 } |
| 223 | 216 |
| 224 // Return a function to check that the rendered result matches the expecte
d result. | 217 // Return a function to check that the rendered result matches the expecte
d result. |
| 225 function checkResult(config) { | 218 function checkResult(should, config) { |
| 226 return function (renderedBuffer) { | 219 return function (renderedBuffer) { |
| 227 var success = true; | 220 var success = true; |
| 228 | 221 |
| 229 actualResult = renderedBuffer.getChannelData(0); | 222 actualResult = renderedBuffer.getChannelData(0); |
| 230 expectedResult = computeExpectedResult(config); | 223 expectedResult = computeExpectedResult(config); |
| 231 | 224 |
| 232 // Compute the SNR and max absolute difference between the actual and
expected result. | 225 // Compute the SNR and max absolute difference between the actual and
expected result. |
| 233 var SNR = 10*Math.log10(computeSNR(actualResult, expectedResult)); | 226 var SNR = 10*Math.log10(computeSNR(actualResult, expectedResult)); |
| 234 var maxDiff = -1; | 227 var maxDiff = -1; |
| 235 var posn = -1; | 228 var posn = -1; |
| 236 | 229 |
| 237 for (var k = 0; k < actualResult.length; ++k) { | 230 for (var k = 0; k < actualResult.length; ++k) { |
| 238 var diff = Math.abs(actualResult[k] - expectedResult[k]); | 231 var diff = Math.abs(actualResult[k] - expectedResult[k]); |
| 239 if (maxDiff < diff) { | 232 if (maxDiff < diff) { |
| 240 maxDiff = diff; | 233 maxDiff = diff; |
| 241 posn = k; | 234 posn = k; |
| 242 } | 235 } |
| 243 } | 236 } |
| 244 | 237 |
| 245 let prefix = "Curve length " + config.curveLength + ", duration " + co
nfig.curveDuration; | 238 let prefix = "Curve length " + config.curveLength + ", duration " + co
nfig.curveDuration; |
| 246 success = success && Should(prefix + ": SNR", SNR, { | 239 should(SNR, prefix + ": SNR") |
| 247 brief: true | 240 .beGreaterThanOrEqualTo(config.snrThreshold); |
| 248 }).beGreaterThanOrEqualTo(config.snrThreshold); | |
| 249 | 241 |
| 250 success = Should(prefix + ": Max difference", maxDiff) | 242 should(maxDiff, prefix + ": Max difference") |
| 251 .beLessThanOrEqualTo(config.maxErrorThreshold) && success; | 243 .beLessThanOrEqualTo(config.maxErrorThreshold); |
| 252 | |
| 253 var message = "Test: curve length = " + config.curveLength + | |
| 254 "; duration frames = " + | |
| 255 config.curveDuration * sampleRate; | |
| 256 | |
| 257 Should(message, success) | |
| 258 .summarize("passed", "failed"); | |
| 259 } | 244 } |
| 260 } | 245 } |
| 261 | 246 |
| 262 // Compute the expected result based on the config settings. | 247 // Compute the expected result based on the config settings. |
| 263 function computeExpectedResult(config) { | 248 function computeExpectedResult(config) { |
| 264 // The automation curve starts at |curveStartTime| and has duration |cu
rveDuration|. So, | 249 // The automation curve starts at |curveStartTime| and has duration |cu
rveDuration|. So, |
| 265 // the output should be zero until curveStartTime, linearly ramp up fro
m there to | 250 // the output should be zero until curveStartTime, linearly ramp up fro
m there to |
| 266 // |curveValue|, and then be constant 1 from then to the end of the buf
fer. | 251 // |curveValue|, and then be constant 1 from then to the end of the buf
fer. |
| 267 | 252 |
| 268 var expected = new Float32Array(testDurationFrames); | 253 var expected = new Float32Array(testDurationFrames); |
| (...skipping 18 matching lines...) Expand all Loading... |
| 287 for (; k < fullGainFrame; ++k) | 272 for (; k < fullGainFrame; ++k) |
| 288 expected[k] = curveValue; | 273 expected[k] = curveValue; |
| 289 | 274 |
| 290 // Amplitude is one for the rest of the test. | 275 // Amplitude is one for the rest of the test. |
| 291 for (; k < testDurationFrames; ++k) | 276 for (; k < testDurationFrames; ++k) |
| 292 expected[k] = 1; | 277 expected[k] = 1; |
| 293 | 278 |
| 294 return expected; | 279 return expected; |
| 295 } | 280 } |
| 296 | 281 |
| 297 audit.runTasks(); | 282 audit.run(); |
| 298 | |
| 299 successfullyParsed = true; | |
| 300 </script> | 283 </script> |
| 301 </body> | 284 </body> |
| 302 </html> | 285 </html> |
| OLD | NEW |