Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 <!doctype html> | |
| 2 <html> | |
| 3 <head> | |
| 4 <title>Test Interpolation for AudioParam.setValueCurveAtTime</title> | |
| 5 <script src="resources/compatibility.js"></script> | |
| 6 <script src="resources/audio-testing.js"></script> | |
| 7 <script src="../resources/js-test.js"></script> | |
|
hongchan
2015/08/07 17:32:51
js-test.js should be the first.
Raymond Toy
2015/08/07 18:58:32
Done.
| |
| 8 </head> | |
| 9 | |
| 10 <body> | |
| 11 <script> | |
| 12 description("Test Interpolation for AudioParam.setValueCurveAtTime"); | |
| 13 | |
| 14 // Play a constant signal through a gain node that is automated using setV alueCurveAtTime with | |
| 15 // a 2-element curve. The output should be a linear change. | |
| 16 | |
| 17 // Chose a sample rate that is a multiple of 128, the rendering quantum si ze. This makes the | |
|
hongchan
2015/08/07 17:32:51
Chose -> Choose
Raymond Toy
2015/08/07 18:58:32
Done.
| |
| 18 // math work out to be nice numbers. | |
| 19 var sampleRate = 25600; | |
| 20 var testDurationSec = 1; | |
| 21 var testDurationFrames = testDurationSec * sampleRate; | |
| 22 | |
| 23 // Where the curve starts and its duration. This MUST be less than the to tal rendering time. | |
| 24 var curveStartTime = 256 / sampleRate; | |
| 25 var curveDuration = 300 / sampleRate;; | |
| 26 var curveValue = 0.75; | |
| 27 | |
| 28 // At this time, the gain node goes to gain 1. This is used to make sure the value curve is | |
| 29 // propagated correctly until the next event. | |
| 30 var fullGainTime = 0.75; | |
| 31 | |
| 32 // Thresholds use to determine if the test passes; these are experimentall y determined. The | |
| 33 // SNR between the actual and expected result should be at least |snrThres hold|. The maximum | |
| 34 // difference betwen them should not exceed |maxErrorThreshold|. | |
| 35 var snrThreshold = 10000; | |
| 36 var maxErrorThreshold = 0; | |
| 37 | |
| 38 var context; | |
| 39 var actualResult; | |
| 40 var expectedResult; | |
| 41 | |
| 42 var audit = Audit.createTaskRunner(); | |
| 43 | |
| 44 audit.defineTask("initialize", function (done) { | |
| 45 window.jsTestIsAsync = true; | |
|
hongchan
2015/08/07 17:32:51
This can be done right after description() method.
Raymond Toy
2015/08/07 18:58:33
Done. And initialize task deleted.
| |
| 46 done(); | |
| 47 }); | |
| 48 | |
| 49 | |
| 50 // Array of test configs. Each config must specify curveStartTime, curveD uration, | |
| 51 // curveLength, fullGainTime, maxErrorThreshold, and snrThreshold. | |
| 52 var testConfigs = [ | |
|
hongchan
2015/08/07 17:32:51
This can be re-written:
var testConfig = [{
/
Raymond Toy
2015/08/07 18:58:32
Done.
| |
| 53 // The main test | |
| 54 { | |
| 55 curveStartTime: 256 / sampleRate, | |
| 56 curveDuration: 300 / sampleRate, | |
| 57 curveLength: 2, | |
| 58 fullGainTime: 0.75, | |
| 59 maxErrorThreshold: 0, | |
| 60 snrThreshold: 10000 | |
| 61 }, | |
| 62 // Increase the curve length | |
| 63 { | |
| 64 curveStartTime: 256 / sampleRate, | |
| 65 curveDuration: 300 / sampleRate, | |
| 66 curveLength: 3, | |
| 67 fullGainTime: 0.75, | |
| 68 maxErrorThreshold: 0, | |
| 69 snrThreshold: 10000 | |
| 70 }, | |
| 71 // Increase the curve length | |
| 72 { | |
| 73 curveStartTime: 256 / sampleRate, | |
| 74 curveDuration: 300 / sampleRate, | |
| 75 curveLength: 16, | |
| 76 fullGainTime: 0.75, | |
| 77 maxErrorThreshold: 0, | |
| 78 snrThreshold: 10000 | |
| 79 }, | |
| 80 // Increase the curve length | |
| 81 { | |
| 82 curveStartTime: 256 / sampleRate, | |
| 83 curveDuration: 300 / sampleRate, | |
| 84 curveLength: 100, | |
| 85 fullGainTime: 0.75, | |
| 86 maxErrorThreshold: 0, | |
| 87 snrThreshold: 10000 | |
| 88 }, | |
| 89 // Corner case with duration less than a frame! | |
| 90 { | |
| 91 curveStartTime: 256 / sampleRate, | |
| 92 curveDuration: 0.25 / sampleRate, | |
| 93 curveLength: 2, | |
| 94 fullGainTime: 0.75, | |
| 95 maxErrorThreshold: 0, | |
| 96 snrThreshold: 10000 | |
| 97 }, | |
| 98 // Short duration test | |
| 99 { | |
| 100 curveStartTime: 256 / sampleRate, | |
| 101 curveDuration: 2 / sampleRate, | |
| 102 curveLength: 2, | |
| 103 fullGainTime: 0.75, | |
| 104 maxErrorThreshold: 0, | |
| 105 snrThreshold: 10000 | |
| 106 }, | |
| 107 // Short duration test with many points. | |
| 108 { | |
| 109 curveStartTime: 256 / sampleRate, | |
| 110 curveDuration: 2 / sampleRate, | |
| 111 curveLength: 8, | |
| 112 fullGainTime: 0.75, | |
| 113 maxErrorThreshold: 0, | |
| 114 snrThreshold: 10000 | |
| 115 }, | |
| 116 // Long duration, big curve | |
| 117 { | |
| 118 curveStartTime: 256 / sampleRate, | |
| 119 curveDuration: .5, | |
| 120 curveLength: 1000, | |
| 121 fullGainTime: 0.75, | |
| 122 maxErrorThreshold: 0, | |
| 123 snrThreshold: 10000 | |
| 124 } | |
| 125 ]; | |
| 126 | |
| 127 // Creates a function based on the test config that is suitable for use by defineTask(). | |
| 128 function createTaskFunction (config) { | |
|
hongchan
2015/08/07 17:32:51
[Optional] Usually the named function does not hav
Raymond Toy
2015/08/07 18:58:33
Done.
| |
| 129 return function (done) { | |
|
hongchan
2015/08/07 17:32:51
The anonymous function can have a space between th
Raymond Toy
2015/08/07 18:58:33
Acknowledged.
| |
| 130 runTest(config).then(done); | |
| 131 }; | |
| 132 } | |
| 133 | |
| 134 // Define a task for each config, in the order listed in testConfigs. | |
| 135 for (var k = 0; k < testConfigs.length; ++k) { | |
| 136 var config = testConfigs[k]; | |
| 137 var name = k + ":curve=" + config.curveLength + ",duration=" + (config.c urveDuration * sampleRate); | |
| 138 audit.defineTask(name, createTaskFunction(config)); | |
| 139 } | |
| 140 | |
| 141 // Must be the last defined task. | |
| 142 audit.defineTask("end", function (done) { | |
| 143 finishJSTest(); | |
| 144 done(); | |
| 145 }); | |
| 146 | |
| 147 function runTest (config) { | |
|
hongchan
2015/08/07 17:32:51
No space between the name in the parentheses. The
Raymond Toy
2015/08/07 18:58:32
Done.
| |
| 148 context = new OfflineAudioContext(1, testDurationFrames, sampleRate); | |
| 149 | |
| 150 // A constant audio source of value 1. | |
| 151 var source = context.createBufferSource(); | |
| 152 source.buffer = createConstantBuffer(context, 1, 1); | |
| 153 source.loop = true; | |
| 154 | |
| 155 // The value curve for testing. Just to make things easy for testing, m ake the curve a | |
| 156 // simple ramp up to curveValue. | |
| 157 // TODO: Maybe allow more complicated curves? | |
|
hongchan
2015/08/07 17:32:51
TODO(rtoy)
Raymond Toy
2015/08/07 18:58:32
Done.
| |
| 158 var curve = new Float32Array(config.curveLength); | |
| 159 for (var k = 0; k < config.curveLength; ++k) { | |
| 160 curve[k] = curveValue / (config.curveLength - 1) * k; | |
| 161 } | |
| 162 | |
| 163 // A gain node that is to be automated using setValueCurveAtTime. | |
| 164 var gain = context.createGain(); | |
| 165 gain.gain.value = 0; | |
| 166 gain.gain.setValueCurveAtTime(curve, config.curveStartTime, config.curve Duration); | |
| 167 // This is to verify that setValueCurveAtTime ends appropriately. | |
| 168 gain.gain.setValueAtTime(1, config.fullGainTime); | |
| 169 | |
| 170 source.connect(gain); | |
| 171 gain.connect(context.destination); | |
| 172 source.start(); | |
| 173 | |
| 174 // Some consistency checks on the test parameters | |
| 175 Should("Check: Curve end time", config.curveStartTime + config.curveDura tion) | |
| 176 .beLessThanOrEqualTo(testDurationSec); | |
| 177 Should("Check: Full gain start time", config.fullGainTime).beLessThanOrE qualTo(testDurationSec); | |
| 178 Should("Check: Full gain start time", config.fullGainTime).beGreaterThan OrEqualTo(config.curveStartTime + config.curveDuration); | |
| 179 | |
| 180 // Rock and roll! | |
| 181 return context.startRendering().then(checkResult(config)); | |
| 182 } | |
| 183 | |
| 184 // Return a function to check that the rendered result matches the expecte d result. | |
| 185 function checkResult(config) | |
|
hongchan
2015/08/07 17:32:51
Let's move the curly brace one line up.
Raymond Toy
2015/08/07 18:58:33
Done.
| |
| 186 { | |
| 187 return function (renderedBuffer) { | |
| 188 var success = true; | |
| 189 | |
| 190 actualResult = renderedBuffer.getChannelData(0); | |
| 191 expectedResult = computeExpectedResult(config); | |
| 192 | |
| 193 // Compute the SNR and max absolute difference between the actual and expected result. | |
| 194 var SNR = 10*Math.log10(computeSNR(actualResult, expectedResult)); | |
| 195 var maxDiff = -1; | |
| 196 var posn = -1; | |
| 197 | |
| 198 for (var k = 0; k < actualResult.length; ++k) { | |
| 199 var diff = Math.abs(actualResult[k] - expectedResult[k]); | |
| 200 if (maxDiff < diff) { | |
| 201 maxDiff = diff; | |
| 202 posn = k; | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 success = success && Should("SNR", SNR).beGreaterThanOrEqualTo(config. snrThreshold); | |
| 207 | |
| 208 if (maxDiff <= config.maxErrorThreshold) { | |
| 209 testPassed("Max difference is less than or equal to " + config.maxEr rorThreshold + "."); | |
| 210 } else { | |
| 211 testFailed("Max difference (" + maxDiff + ") NOT less than or equal to " + | |
| 212 config.maxErrorThreshold + " at frame " + posn + "."); | |
| 213 success = false; | |
| 214 } | |
| 215 | |
| 216 var message = "Test: curve length = " + config.curveLength + "; durati on frames = " + | |
| 217 config.curveDuration * sampleRate + ".\n"; | |
| 218 | |
| 219 if (success) | |
| 220 testPassed(message); | |
| 221 else | |
| 222 testFailed(message); | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 // Compute the expected result based on the config settings. | |
| 227 function computeExpectedResult(config) | |
| 228 { | |
| 229 // The automation curve starts at |curveStartTime| and has duration |cu rveDuration|. So, | |
| 230 // the output should be zero until curveStartTime, linearly ramp up fro m there to | |
| 231 // |curveValue|, and then be constant 1 from then to the end of the buf fer. | |
| 232 | |
| 233 var expected = new Float32Array(testDurationFrames); | |
| 234 | |
| 235 var curveStartFrame = config.curveStartTime * sampleRate; | |
| 236 var curveEndFrame = Math.floor((config.curveStartTime + config.curveDur ation) * sampleRate); | |
| 237 var fullGainFrame = config.fullGainTime * sampleRate; | |
| 238 | |
| 239 // TODO(crbug.com/517491): setValueAtTime is off by one rendering quant um. | |
| 240 fullGainFrame += 128; | |
| 241 | |
| 242 var k; | |
| 243 | |
| 244 // Zero out the start. | |
| 245 for (k = 0; k < curveStartFrame; ++k) { | |
|
hongchan
2015/08/07 17:32:51
One-line loop block does not curly embraces around
Raymond Toy
2015/08/07 18:58:32
Done.
| |
| 246 expected[k] = 0; | |
| 247 } | |
| 248 | |
| 249 // Linearly ramp now. This assumes that the actual curve used is a lin ear ramp, even if | |
| 250 // there are many curve points. | |
| 251 var stepSize = curveValue / (config.curveDuration * sampleRate - 1); | |
| 252 for (; k < curveEndFrame; ++k) { | |
|
hongchan
2015/08/07 17:32:51
ditto.
Raymond Toy
2015/08/07 18:58:32
Done.
| |
| 253 expected[k] = stepSize * (k - curveStartFrame); | |
| 254 } | |
| 255 | |
| 256 // Hold it constant until the next event | |
| 257 for (; k < fullGainFrame; ++k) { | |
|
hongchan
2015/08/07 17:32:51
ditto.
Raymond Toy
2015/08/07 18:58:32
Done.
| |
| 258 expected[k] = curveValue; | |
| 259 } | |
| 260 | |
| 261 // Amplitude is one for the rest of the test. | |
| 262 for (; k < testDurationFrames; ++k) { | |
|
hongchan
2015/08/07 17:32:51
ditto.
Raymond Toy
2015/08/07 18:58:32
Done.
| |
| 263 expected[k] = 1; | |
| 264 } | |
| 265 | |
| 266 return expected; | |
| 267 } | |
| 268 | |
| 269 //runTest(); | |
|
hongchan
2015/08/07 17:32:51
Why is this commented out? Let's remove if this is
Raymond Toy
2015/08/07 18:58:33
Done.
| |
| 270 | |
| 271 audit.runTasks(); | |
| 272 | |
| 273 successfullyParsed = true; | |
| 274 </script> | |
| 275 </body> | |
| 276 </html> | |
| OLD | NEW |