| <!DOCTYPE html> |
| <html> |
| <head> |
| <title> |
| Updating of Value Attribute from Timeline |
| </title> |
| <script src="../../imported/w3c/web-platform-tests/resources/testharness.js"></script> |
| <script src="../../resources/testharnessreport.js"></script> |
| <script src="../resources/audit-util.js"></script> |
| <script src="../resources/audit.js"></script> |
| <script src="../resources/audio-param.js"></script> |
| </head> |
| <body> |
| <script id="layout-test-code"> |
| // This should be a power of two so that all time computations have no |
| // round-off errors. |
| let sampleRate = 32768; |
| let renderQuantumSize = 128; |
| // How many tests to run. |
| let renderLoops = 20; |
| let renderFrames = renderLoops * renderQuantumSize; |
| let renderDuration = renderFrames / sampleRate; |
| |
| let audit = Audit.createTaskRunner(); |
| |
| audit.define('linear', (task, should) => { |
| // Test the value attribute from a linearRamp event |
| runTest(should, function(g, v0, t0, v1, t1) { |
| g.gain.linearRampToValueAtTime(v1, t1); |
| return { |
| expectedValue: function(testTime) { |
| return audioParamLinearRamp(testTime, v0, t0, v1, t1); |
| }, |
| message: 'linearRamp(' + v1 + ', ' + t1 + ')', |
| errorThreshold: 1.1650e-6 |
| }; |
| }).then(() => task.done()); |
| }); |
| |
| audit.define('exponential', (task, should) => { |
| // Test the value attribute from an exponentialRamp event |
| runTest(should, function(g, v0, t0, v1, t1) { |
| g.gain.exponentialRampToValueAtTime(v1, t1); |
| return { |
| expectedValue: function(testTime) { |
| return audioParamExponentialRamp(testTime, v0, t0, v1, t1); |
| }, |
| message: 'exponentialRamp(' + v1 + ', ' + t1 + ')', |
| errorThreshold: 7.4601e-7 |
| }; |
| }).then(() => task.done()); |
| }); |
| |
| audit.define('setTarget', (task, should) => { |
| // Test the value attribute from a setTargetAtTime event |
| runTest(should, function(g, v0, t0, v1, t1) { |
| let timeConstant = 0.1; |
| let vFinal = 0; |
| g.gain.setTargetAtTime(vFinal, t0, timeConstant); |
| return { |
| expectedValue: function(testTime) { |
| return audioParamSetTarget( |
| testTime, v0, t0, vFinal, timeConstant); |
| }, |
| message: 'setTargetAtTime(' + vFinal + ', ' + t0 + ', ' + |
| timeConstant + ')', |
| errorThreshold: 2.2599e-6 |
| }; |
| }).then(() => task.done()); |
| }); |
| |
| audit.define('setValueCurve', (task, should) => { |
| // Test the value attribute from a setValueCurve event |
| runTest(should, function(g, v0, t0, v1, t1) { |
| let curve = [1, 1.5, 4]; |
| let duration = t1 - t0; |
| g.gain.setValueCurveAtTime(Float32Array.from(curve), t0, duration); |
| return { |
| expectedValue: function(testTime) { |
| return audioParamSetValueCurve(testTime, curve, t0, duration); |
| }, |
| message: 'setValueCurveAtTime([' + curve + '], ' + t0 + ', ' + |
| duration + ')', |
| errorThreshold: 7.9577e-8 |
| }; |
| }).then(() => task.done()); |
| }); |
| |
| audit.run(); |
| |
| // Test that the .value getter has the correct value when a timeline is |
| // running. The |testFunction| is the underlying test to be run. |
| function runTest(should, testFunction) { |
| // Create a simple graph consisting of a constant source and a gain node |
| // where the automations are run. A setValueAtTime event starts things |
| // off. |
| let context = new OfflineAudioContext(1, renderFrames, sampleRate); |
| let source = context.createBufferSource(); |
| source.buffer = createConstantBuffer(context, 1, 1); |
| source.loop = true; |
| let gain = context.createGain(); |
| |
| source.connect(gain); |
| gain.connect(context.destination); |
| |
| // Start the timeline with setValueAtTime(v0, t0). |
| let v0 = 0.25; |
| let t0 = 0; |
| // End value and time, for those that need it. The end time is less |
| // than the rendering duration so we can test that the final values of |
| // the automation (if any) are also correct. |
| let v1 = 100; |
| let t1 = renderDuration - 5 * renderQuantumSize / sampleRate; |
| |
| gain.gain.setValueAtTime(v0, t0); |
| |
| // Run the desired automation test. The test function returns a |
| // dictionary consisting of the following properties: |
| // |
| // |message| an informative message about the automation being |
| // tested. |errorThreshold| error threshold to determine if the test |
| // passes or not. |expectedValue| a function that compute the expected |
| // value at time |t|. |
| let test = testFunction(gain, v0, t0, v1, t1); |
| |
| // Print an informative message about the test being run. |
| // testPassed("Initialize " + test.message + " with setValueAtTime(" + |
| // v0 + ", " + t0 + ")."); |
| should(true, 'Initialize') |
| .message( |
| test.message + ' with setValueAtTime(' + v0 + ', ' + t0 + ')', |
| ''); |
| |
| let success = true; |
| |
| // Max relative error found for this test. This is printed if the test |
| // fails so that setting the thresholds is easier. |
| let maxError = 0; |
| |
| // For every rendering quantum (except the first), suspend the context |
| // so the we can inspect the value attribute and compare it with the |
| // expected value. |
| for (let k = 1; k < renderLoops; ++k) { |
| let time = k * renderQuantumSize / sampleRate; |
| context.suspend(time) |
| .then(function() { |
| // The context is supsended at time |time|, which is just before |
| // the rendering quantum starts. Thus, the value of the |
| // attribute is actually 1 frame before the current context |
| // time. |
| let sampleTime = context.currentTime - 1 / sampleRate; |
| |
| // Compute the max relative error |
| let expected = test.expectedValue(sampleTime); |
| let relError = |
| Math.abs(expected - gain.gain.value) / Math.abs(expected); |
| maxError = Math.max(relError, maxError); |
| |
| success = |
| should( |
| gain.gain.value, |
| test.message + ' at frame ' + (sampleRate * sampleTime)) |
| .beCloseTo( |
| expected, {threshold: test.errorThreshold || 0}); |
| }) |
| .then(context.resume.bind(context)); |
| } |
| |
| source.start(); |
| |
| return context.startRendering().then(function(resultBuffer) { |
| // Just print a final pass (or fail) message. |
| should(success, 'Gain .value attribute for ' + test.message) |
| .message( |
| 'correctly updated during automation', |
| 'not correctly updated during automation; max error = ' + |
| maxError); |
| }); |
| } |
| </script> |
| </body> |
| </html> |