Changeset 109518 in webkit
- Timestamp:
- Mar 2, 2012 12:01:01 AM (12 years ago)
- Location:
- trunk
- Files:
-
- 8 added
- 7 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r109516 r109518 1 2012-03-02 Raymond Toy <rtoy@google.com> 2 3 AudioParam needs tests for the parameter automation routines. 4 https://bugs.webkit.org/show_bug.cgi?id=77666 5 6 Reviewed by Chris Rogers. 7 8 * webaudio/audioparam-exponentialRampToValueAtTime.html: 9 * webaudio/audioparam-linearRampToValueAtTime-expected.txt: Added. 10 * webaudio/audioparam-linearRampToValueAtTime.html: Added. 11 * webaudio/audioparam-setTargetValueAtTime-expected.txt: Added. 12 * webaudio/audioparam-setTargetValueAtTime.html: Added. 13 * webaudio/audioparam-setValueAtTime-expected.txt: Added. 14 * webaudio/audioparam-setValueAtTime.html: Added. 15 * webaudio/audioparam-setValueCurveAtTime-expected.txt: Added. 16 * webaudio/audioparam-setValueCurveAtTime.html: Added. 17 * webaudio/resources/audioparam-testing.js: 18 (renderLength): 19 (createConstantArray): 20 (createLinearRampArray): 21 (discreteTimeConstantForSampleRate): 22 (createExponentialApproachArray): 23 (createSineWaveArray): 24 (endValueDelta): 25 (valueUpdate): 26 (comparePartialSignals): 27 (verifyDiscontinuities): 28 (compareSignals): 29 (checkResultFunction.return.var): 30 (checkResultFunction): 31 (doAutomation): 32 (createAudioGraphAndTest): 33 * webaudio/resources/audio-testing.js: Moved isValidNumber from 34 biquad-testing.js to here. 35 * webaudio/resources/biquad-testing.js: Moved isValidNumber from 36 here to audio-testing.js. 37 1 38 2012-03-01 Dirk Pranke <dpranke@chromium.org> 2 39 -
trunk/LayoutTests/webaudio/audioparam-exponentialRampToValueAtTime.html
r108592 r109518 21 21 // decreasing ramp for each interval. 22 22 23 // Number of tests to run. 24 var numberOfTests = 100; 25 23 26 // Max allowed difference between the rendered data and the expected result. 24 27 var maxAllowedError = 6.75e-4; 25 28 26 // Information about the starting and ending times and starting and ending values for each27 // time interval.28 var timeValueInfo;29 30 29 // The AudioGainNode starts with this value instead of the default value. 31 30 var initialValue = 100; 32 33 // The difference between starting values between each time interval.34 var startingValueDelta = initialValue / numberOfTests;35 36 // Each exponential ramp will increase or decrease by this much from the start to the end of37 // the time interval. We choose half of startingValueDelta so that the ending value of the38 // ramp will be distinct from the starting value of the ramp for next time interval. This39 // allows us to detect where the ramp begins and ends.40 var startEndValueChange = startingValueDelta / 2;41 42 // Threshold to use for detecting discontinuities that should appear at each time interval.43 var discontinuityThreshold = startEndValueChange / 2;44 45 var gainNode;46 47 var constantBuffer;48 var bufferSource;49 50 // Return the difference between the starting value and the ending value for time interval51 // |timeIntervalIndex|. We alternate between an end value that is above or below the starting52 // value.53 function endValueDelta(timeIntervalIndex)54 {55 if (timeIntervalIndex & 1) {56 return -startEndValueChange;57 } else {58 return startEndValueChange;59 }60 }61 31 62 32 // Set the gain node value to the specified value at the specified time. … … 74 44 } 75 45 76 // Return the difference between the starting value at |timeIntervalIndex| and the starting77 // value at the next time interval. Since we started at a large initial value, we decrease78 // the value at each time interval.79 function valueUpdate(timeIntervalIndex)80 {81 return -startingValueDelta;82 }83 84 function checkResult(event)85 {86 var buffer = event.renderedBuffer;87 renderedData = buffer.getChannelData(0);88 89 compareSignals("exponentialRampToValueAtTime()", maxAllowedError, renderedData, createExponentialRampArray, timeValueInfo, discontinuityThreshold);90 91 finishJSTest();92 }93 94 46 function runTest() 95 47 { 96 if (window.layoutTestController) { 97 layoutTestController.dumpAsText(); 98 layoutTestController.waitUntilDone(); 99 } 48 createAudioGraphAndTest(numberOfTests, 49 initialValue, 50 setValue, 51 generateRamp, 52 "exponentialRampToValueAtTime()", 53 maxAllowedError, 54 createExponentialRampArray); 100 55 101 window.jsTestIsAsync = true;102 103 // Create offline audio context.104 context = new webkitAudioContext(2, renderLength, sampleRate);105 constantBuffer = createConstantBuffer(context, 1, renderLength);106 107 // We use an AudioGainNode here simply as a convenient way to test the AudioParam108 // automation, since it's easy to pass a constant value through the node, automate the109 // .gain attribute and observe the resulting values.110 111 gainNode = context.createGainNode();112 113 bufferSource = context.createBufferSource();114 bufferSource.buffer = constantBuffer;115 bufferSource.connect(gainNode);116 gainNode.connect(context.destination);117 118 // At each time interval, we adjust the value by startingValueDelta. However, the119 // exponential ramp end value is the value +/- startEndValueChange. (We alternately120 // specify the end value to be greater or smaller than the starting value, to test the121 // ramp going up and down.)122 //123 // By doing it this way, we get a discontinuity at each time interval which we can use to124 // test if the ramp started and ended at the correct time.125 timeValueInfo = doAutomation(initialValue,126 endValueDelta,127 setValue,128 generateRamp,129 valueUpdate);130 131 bufferSource.noteOn(0);132 133 context.oncomplete = checkResult;134 context.startRendering();135 56 } 136 57 -
trunk/LayoutTests/webaudio/resources/audio-testing.js
r108604 r109518 129 129 return endFrame - startFrame; 130 130 } 131 132 // True if the number is not an infinity or NaN 133 function isValidNumber(x) { 134 return !isNaN(x) && (x != Infinity) && (x != -Infinity); 135 } -
trunk/LayoutTests/webaudio/resources/audioparam-testing.js
r108592 r109518 1 1 var sampleRate = 44100; 2 2 3 // Number of tests to run. 4 var numberOfTests = 100; 5 6 // Time interval between value changes. It is best if 1 / numberOfTests is not close to 7 // timeInterval. 3 // Information about the starting/ending times and starting/ending values for each time interval. 4 var timeValueInfo; 5 6 // The difference between starting values between each time interval. 7 var startingValueDelta; 8 9 // For any automation function that has an end or target value, the end value is based the starting 10 // value of the time interval. The starting value will be increased or decreased by 11 // |startEndValueChange|. We choose half of |startingValueDelta| so that the ending value will be 12 // distinct from the starting value for next time interval. This allows us to detect where the ramp 13 // begins and ends. 14 var startEndValueChange; 15 16 // Default threshold to use for detecting discontinuities that should appear at each time interval. 17 var discontinuityThreshold; 18 19 // Time interval between value changes. It is best if 1 / numberOfTests is not close to timeInterval. 8 20 var timeInterval = .03; 9 21 22 // Some suitable time constant so that we can see a significant change over a timeInterval. This is 23 // only needed by setTargetValueAtTime() which needs a time constant. 24 var timeConstant = timeInterval / 3; 25 26 var gainNode; 27 28 var context; 29 10 30 // Make sure we render long enough to capture all of our test data. 11 var renderTime = (numberOfTests + 1) * timeInterval; 12 var renderLength = timeToSampleFrame(renderTime, sampleRate); 13 14 var context; 31 function renderLength(numberOfTests) 32 { 33 return timeToSampleFrame((numberOfTests + 1) * timeInterval, sampleRate); 34 } 15 35 16 36 // Create a buffer containing the same constant value. … … 27 47 } 28 48 49 // Create a constant reference signal with the given |value|. Basically the same as 50 // |createConstantBuffer|, but with the parameters to match the other create functions. The 51 // |endValue| is ignored. 52 function createConstantArray(startTime, endTime, value, endValue, sampleRate) 53 { 54 var startFrame = timeToSampleFrame(startTime, sampleRate); 55 var endFrame = timeToSampleFrame(endTime, sampleRate); 56 var length = endFrame - startFrame; 57 58 var buffer = createConstantBuffer(context, value, length); 59 60 return buffer.getChannelData(0); 61 } 62 63 // Create a linear ramp starting at |startValue| and ending at |endValue|. The ramp starts at time 64 // |startTime| and ends at |endTime|. (The start and end times are only used to compute how many 65 // samples to return.) 66 function createLinearRampArray(startTime, endTime, startValue, endValue, sampleRate) 67 { 68 var startFrame = timeToSampleFrame(startTime, sampleRate); 69 var endFrame = timeToSampleFrame(endTime, sampleRate); 70 var length = endFrame - startFrame; 71 var array = new Array(length); 72 73 var step = (endValue - startValue) / length; 74 75 for (k = 0; k < length; ++k) { 76 array[k] = startValue + k * step; 77 } 78 79 return array; 80 } 81 29 82 // Create an exponential ramp starting at |startValue| and ending at |endValue|. The ramp starts at 30 83 // time |startTime| and ends at |endTime|. (The start and end times are only used to compute how … … 35 88 var endFrame = timeToSampleFrame(endTime, sampleRate); 36 89 var length = endFrame - startFrame; 37 var buffer= new Array(length);90 var array = new Array(length); 38 91 39 92 var multiplier = Math.pow(endValue / startValue, 1 / length); 40 93 41 94 for (var k = 0; k < length; ++k) { 42 buffer[k] = startValue * Math.pow(multiplier, k); 43 } 44 45 return buffer; 95 array[k] = startValue * Math.pow(multiplier, k); 96 } 97 98 return array; 99 } 100 101 function discreteTimeConstantForSampleRate(timeConstant, sampleRate) 102 { 103 return 1 - Math.exp(-1 / (sampleRate * timeConstant)); 104 } 105 106 // Create a signal that starts at |startValue| and exponentially approaches the target value of 107 // |targetValue|, using a time constant of |timeConstant|. The ramp starts at time |startTime| and 108 // ends at |endTime|. (The start and end times are only used to compute how many samples to 109 // return.) 110 function createExponentialApproachArray(startTime, endTime, startValue, targetValue, sampleRate, timeConstant) 111 { 112 var startFrame = timeToSampleFrame(startTime, sampleRate); 113 var endFrame = timeToSampleFrame(endTime, sampleRate); 114 var length = endFrame - startFrame; 115 var array = new Array(length); 116 var c = discreteTimeConstantForSampleRate(timeConstant, sampleRate); 117 118 var value = startValue; 119 120 for (var k = 0; k < length; ++k) { 121 array[k] = value; 122 value += (targetValue - value) * c; 123 } 124 125 return array; 126 } 127 128 // Create a sine wave of the given frequency and amplitude. The sine wave is offset by half the 129 // amplitude so that result is always positive. 130 function createSineWaveArray(durationSeconds, freqHz, amplitude, sampleRate) 131 { 132 var length = timeToSampleFrame(durationSeconds, sampleRate); 133 var signal = new Float32Array(length); 134 var omega = 2 * Math.PI * freqHz / sampleRate; 135 var halfAmplitude = amplitude / 2; 136 137 for (var k = 0; k < length; ++k) { 138 signal[k] = halfAmplitude + halfAmplitude * Math.sin(omega * k); 139 } 140 141 return signal; 142 } 143 144 // Return the difference between the starting value and the ending value for time interval 145 // |timeIntervalIndex|. We alternate between an end value that is above or below the starting 146 // value. 147 function endValueDelta(timeIntervalIndex) 148 { 149 if (timeIntervalIndex & 1) { 150 return -startEndValueChange; 151 } else { 152 return startEndValueChange; 153 } 154 } 155 156 // Return the difference between the starting value at |timeIntervalIndex| and the starting value at 157 // the next time interval. Since we started at a large initial value, we decrease the value at each 158 // time interval. 159 function valueUpdate(timeIntervalIndex) 160 { 161 return -startingValueDelta; 46 162 } 47 163 … … 50 166 { 51 167 var startSample = timeToSampleFrame(startTime, sampleRate); 52 var expected = expectedFunction(startTime, endTime, valueInfo.startValue, valueInfo.endValue, sampleRate );168 var expected = expectedFunction(startTime, endTime, valueInfo.startValue, valueInfo.endValue, sampleRate, timeConstant); 53 169 54 170 var n = expected.length; … … 57 173 58 174 for (var k = 0; k < n; ++k) { 175 // Make sure we don't pass these tests because a NaN has been generated in either the 176 // rendered data or the reference data. 177 if (!isValidNumber(rendered[startSample + k])) { 178 maxError = Infinity; 179 maxErrorIndex = startSample + k; 180 testFailed("NaN or infinity for rendered data at " + maxErrorIndex); 181 break; 182 } 183 if (!isValidNumber(expected[k])) { 184 maxError = Infinity; 185 maxErrorIndex = startSample + k; 186 testFailed("Nan or infinity for reference data at " + maxErrorIndex); 187 break; 188 } 59 189 var error = Math.abs(rendered[startSample + k] - expected[k]); 60 190 if (error > maxError) { … … 84 214 } 85 215 216 var testCount; 217 218 // If there are numberOfTests intervals, there are only numberOfTests - 1 internal interval 219 // boundaries. Hence the maximum number of discontinuties we expect to find is numberOfTests - 220 // 1. If we find more than that, we have no reference to compare against. We also assume that 221 // the actual discontinuities are close to the expected ones. 222 // 223 // This is just a sanity check when something goes really wrong. For example, if the threshold 224 // is too low, every sample frame looks like a discontinuity. 225 if (breaks.length >= numberOfTests) { 226 testCount = numberOfTests - 1; 227 testFailed("Found more discontinuities (" + breaks.length + ") than expected. Only comparing first " + testCount + "discontinuities."); 228 success = false; 229 } else { 230 testCount = breaks.length; 231 } 232 86 233 // Compare the location of each discontinuity with the end time of each interval. (There is no 87 234 // discontinuity at the start of the signal.) 88 for (var k = 0; k < breaks.length; ++k) {235 for (var k = 0; k < testCount; ++k) { 89 236 var expectedSampleFrame = timeToSampleFrame(times[k + 1], sampleRate); 90 237 if (breaks[k] != expectedSampleFrame) { … … 97 244 if (badLocations) { 98 245 testFailed(badLocations + " discontinuities at incorrect locations"); 246 success = false; 99 247 } else { 100 testPassed("All " + (breaks.length + 1) + " tests started and ended at the correct time."); 248 if (breaks.length == numberOfTests - 1) { 249 testPassed("All " + numberOfTests + " tests started and ended at the correct time."); 250 } else { 251 testFailed("Found " + breaks.length + " discontinuities but expected " + (numberOfTests - 1)); 252 success = false; 253 } 101 254 } 102 255 … … 132 285 133 286 if (result.maxError > maxError) { 134 testFailed("Incorrect gainfor test " + k + ". Max error = " + result.maxError + " at offset " + (result.index + timeToSampleFrame(times[k], sampleRate)));287 testFailed("Incorrect value for test " + k + ". Max error = " + result.maxError + " at offset " + (result.index + timeToSampleFrame(times[k], sampleRate))); 135 288 ++failedTestCount; 136 289 } … … 151 304 } 152 305 306 // Create a function to test the rendered data with the reference data. 307 // 308 // testName - string describing the test 309 // 310 // error - max allowed error between rendered data and the reference data. 311 // 312 // referenceFunction - function that generates the reference data to be compared with the rendered 313 // data. 314 // 315 // jumpThreshold - optional parameter that specifies the threshold to use for detecting 316 // discontinuities. If not specified, defaults to discontinuityThreshold. 317 // 318 function checkResultFunction(testName, error, referenceFunction, jumpThreshold) 319 { 320 return function(event) { 321 var buffer = event.renderedBuffer; 322 renderedData = buffer.getChannelData(0); 323 324 var threshold; 325 326 if (!jumpThreshold) { 327 threshold = discontinuityThreshold; 328 } else { 329 threshold = jumpThreshold; 330 } 331 332 compareSignals(testName, error, renderedData, referenceFunction, timeValueInfo, threshold); 333 334 finishJSTest(); 335 } 336 } 337 153 338 // Run all the automation tests. 154 339 // 340 // numberOfTests - number of tests (time intervals) to run. 341 // 155 342 // initialValue - The initial value of the first time interval. 156 //157 // endValueDeltaFunction - How much the end value differs from the value at the start of the158 // interval.159 343 // 160 344 // setValueFunction - function that sets the specified value at the start of a time interval. … … 163 347 // the value approaches the end value. 164 348 // 165 // valueUpdateFunction - The starting value at time interval k is adjusted by valueUpdateFunction(k) to give the new starting value at time interval k + 1.166 //167 349 // An object is returned containing an array of start times for each time interval, and an array 168 350 // giving the start and end values for the interval. 169 function doAutomation( initialValue, endValueDeltaFunction, setValueFunction, automationFunction, valueUpdateFunction)351 function doAutomation(numberOfTests, initialValue, setValueFunction, automationFunction) 170 352 { 171 353 var timeInfo = [0]; … … 176 358 var startTime = k * timeInterval; 177 359 var endTime = (k + 1) * timeInterval; 178 var endValue = value + endValueDelta Function(k);360 var endValue = value + endValueDelta(k); 179 361 180 362 // Set the value at the start of the time interval. 181 363 setValueFunction(value, startTime); 182 364 183 // Specify the target value, and how we should approach the target value.365 // Specify the end or target value, and how we should approach it. 184 366 automationFunction(endValue, startTime, endTime); 185 367 … … 188 370 valueInfo.push({startValue: value, endValue : endValue}); 189 371 190 value += valueUpdate Function(k);372 value += valueUpdate(k); 191 373 } 192 374 193 375 return {times : timeInfo, values : valueInfo}; 194 376 } 377 378 // Create the audio graph for the test and then run the test. 379 // 380 // numberOfTests - number of time intervals (tests) to run. 381 // 382 // initialValue - the initial value of the gain at time 0. 383 // 384 // setValueFunction - function to set the value at the beginning of each time interval. 385 // 386 // automationFunction - the AudioParamTimeline automation function 387 // 388 // testName - string indicating the test that is being run. 389 // 390 // maxError - maximum allowed error between the rendered data and the reference data 391 // 392 // referenceFunction - function that generates the reference data to be compared against the 393 // rendered data. 394 // 395 // jumpThreshold - optional parameter that specifies the threshold to use for detecting 396 // discontinuities. If not specified, defaults to discontinuityThreshold. 397 // 398 function createAudioGraphAndTest(numberOfTests, initialValue, setValueFunction, automationFunction, testName, maxError, referenceFunction, jumpThreshold) 399 { 400 if (window.layoutTestController) { 401 layoutTestController.dumpAsText(); 402 layoutTestController.waitUntilDone(); 403 } 404 405 window.jsTestIsAsync = true; 406 407 // Create offline audio context. 408 context = new webkitAudioContext(2, renderLength(numberOfTests), sampleRate); 409 var constantBuffer = createConstantBuffer(context, 1, renderLength(numberOfTests)); 410 411 // We use an AudioGainNode here simply as a convenient way to test the AudioParam 412 // automation, since it's easy to pass a constant value through the node, automate the 413 // .gain attribute and observe the resulting values. 414 415 gainNode = context.createGainNode(); 416 417 var bufferSource = context.createBufferSource(); 418 bufferSource.buffer = constantBuffer; 419 bufferSource.connect(gainNode); 420 gainNode.connect(context.destination); 421 422 // Set up default values for the parameters that control how the automation test values progress 423 // for each time interval. 424 startingValueDelta = initialValue / numberOfTests; 425 startEndValueChange = startingValueDelta / 2; 426 discontinuityThreshold = startEndValueChange / 2; 427 428 // Run the automation tests. 429 timeValueInfo = doAutomation(numberOfTests, 430 initialValue, 431 setValueFunction, 432 automationFunction); 433 bufferSource.noteOn(0); 434 435 context.oncomplete = checkResultFunction(testName, 436 maxError, 437 referenceFunction, 438 jumpThreshold); 439 context.startRendering(); 440 } -
trunk/LayoutTests/webaudio/resources/biquad-testing.js
r109458 r109518 481 481 } 482 482 483 // True if the number is not an infinity or NaN484 function isValidNumber(x) {485 return !isNaN(x) && (x != Infinity) && (x != -Infinity);486 }487 488 483 function checkFilterResponse(filterType, filterParameters) { 489 484 return function(event) { -
trunk/Source/WebCore/ChangeLog
r109515 r109518 1 2012-03-02 Raymond Toy <rtoy@google.com> 2 3 AudioParam needs tests for the parameter automation routines. 4 https://bugs.webkit.org/show_bug.cgi?id=77666 5 6 Reviewed by Chris Rogers. 7 8 Tests: webaudio/audioparam-linearRampToValueAtTime.html 9 webaudio/audioparam-setTargetValueAtTime.html 10 webaudio/audioparam-setValueAtTime.html 11 webaudio/audioparam-setValueCurveAtTime.html 12 13 * webaudio/AudioParamTimeline.cpp: 14 (WebCore::AudioParamTimeline::valuesForTimeRangeImpl): Round the 15 curveIndex to fix timing issue in setValueCurveAtTime. 16 1 17 2012-03-01 Pablo Flouret <pablof@motorola.com> 2 18 -
trunk/Source/WebCore/webaudio/AudioParamTimeline.cpp
r108592 r109518 330 330 // Oversampled curve data can be provided if smoothness is desired. 331 331 for (; writeIndex < fillToFrame; ++writeIndex) { 332 unsigned curveIndex = static_cast<unsigned>(curveVirtualIndex); 332 // Ideally we'd use round() from MathExtras, but we're in a tight loop here 333 // and we're trading off precision for extra speed. 334 unsigned curveIndex = static_cast<unsigned>(0.5 + curveVirtualIndex); 335 333 336 curveVirtualIndex += curvePointsPerFrame; 334 337
Note: See TracChangeset
for help on using the changeset viewer.