| OLD | NEW |
| 1 <!doctype html> | 1 <!DOCTYPE html> |
| 2 <html> | 2 <html> |
| 3 <head> | 3 <head> |
| 4 <title>Test setTargetAtTime Approach to Limit</title> | 4 <title> |
| 5 Test setTargetAtTime Approach to Limit |
| 6 </title> |
| 5 <script src="../../resources/testharness.js"></script> | 7 <script src="../../resources/testharness.js"></script> |
| 6 <script src="../../resources/testharnessreport.js"></script> | 8 <script src="../../resources/testharnessreport.js"></script> |
| 7 <script src="../resources/audit-util.js"></script> | 9 <script src="../resources/audit-util.js"></script> |
| 8 <script src="../resources/audit.js"></script> | 10 <script src="../resources/audit.js"></script> |
| 9 <script src="../resources/audioparam-testing.js"></script> | 11 <script src="../resources/audioparam-testing.js"></script> |
| 10 </head> | 12 </head> |
| 13 <body> |
| 14 <script id="layout-test-code"> |
| 15 let audit = Audit.createTaskRunner(); |
| 11 | 16 |
| 12 <body> | 17 audit.define('approach 1', (task, should) => { |
| 13 <script> | 18 let sampleRate = 48000; |
| 14 | 19 |
| 15 var audit = Audit.createTaskRunner(); | 20 // A really short time constant so that setTargetAtTime approaches the |
| 21 // limiting value well before the end of the test. |
| 22 let timeConstant = 0.001; |
| 16 | 23 |
| 17 audit.define("approach 1", (task, should) => { | 24 // Find the time where setTargetAtTime is close enough to the limit. |
| 18 var sampleRate = 48000; | 25 // Since we're approaching 1, use a value of eps smaller than |
| 19 | 26 // kSetTargetThreshold (5e-7) in AudioParamTimeline.cpp. This is to |
| 20 // A really short time constant so that setTargetAtTime approaches the l
imiting value well | 27 // account for round-off in the actual implementation (which uses a |
| 21 // before the end of the test. | 28 // filter and not the formula.) |
| 22 var timeConstant = 0.001; | 29 let limitThreshold = 1e-7; |
| 23 | |
| 24 // Find the time where setTargetAtTime is close enough to the limit. Si
nce we're | |
| 25 // approaching 1, use a value of eps smaller than kSetTargetThreshold (5
e-7) in | |
| 26 // AudioParamTimeline.cpp. This is to account for round-off in the actu
al implementation | |
| 27 // (which uses a filter and not the formula.) | |
| 28 var limitThreshold = 1e-7; | |
| 29 | 30 |
| 30 runTest(should, { | 31 runTest(should, { |
| 31 sampleRate: sampleRate, | 32 sampleRate: sampleRate, |
| 32 v0: 0, | 33 v0: 0, |
| 33 v1: 1, | 34 v1: 1, |
| 34 timeConstant: timeConstant, | 35 timeConstant: timeConstant, |
| 35 eps: limitThreshold, | 36 eps: limitThreshold, |
| 36 // Experimentally determined | 37 // Experimentally determined |
| 37 threshold: 2.4e-5 | 38 threshold: 2.4e-5 |
| 38 }).then(() => task.done()); | 39 }).then(() => task.done()); |
| 39 }) | 40 }) |
| 40 | 41 |
| 41 audit.define("approach 0", (task, should) => { | 42 audit.define('approach 0', (task, should) => { |
| 42 // Use the equation for setTargetAtTime to figure out when we are close
to 0: | 43 // Use the equation for setTargetAtTime to figure out when we are close |
| 44 // to 0: |
| 43 // | 45 // |
| 44 // v(t) = exp(-t/tau) | 46 // v(t) = exp(-t/tau) |
| 45 // | 47 // |
| 46 // So find t such that exp(-t/tau) <= eps. Thus t >= - tau * log(eps). | 48 // So find t such that exp(-t/tau) <= eps. Thus t >= - tau * log(eps). |
| 47 // | 49 // |
| 48 // For eps, use 1e-20 (kSetTargetZeroThreshold in AudioParamTimeline.cpp
). | 50 // For eps, use 1e-20 (kSetTargetZeroThreshold in |
| 51 // AudioParamTimeline.cpp). |
| 49 | 52 |
| 50 var sampleRate = 48000; | 53 let sampleRate = 48000; |
| 51 | 54 |
| 52 // A really short time constant so that setTargetAtTime approaches the l
imiting value well | 55 // A really short time constant so that setTargetAtTime approaches the |
| 53 // before the end of the test. | 56 // limiting value well before the end of the test. |
| 54 var timeConstant = 0.001; | 57 let timeConstant = 0.001; |
| 55 | 58 |
| 56 // Find the time where setTargetAtTime is close enough to the limit. Si
nce we're | 59 // Find the time where setTargetAtTime is close enough to the limit. |
| 57 // approaching 0, use a value of eps smaller than kSetTargetZeroThreshol
d (1e-20) in | 60 // Since we're approaching 0, use a value of eps smaller than |
| 58 // AudioParamTimeline.cpp. This is to account for round-off in the actu
al implementation | 61 // kSetTargetZeroThreshold (1e-20) in AudioParamTimeline.cpp. This is |
| 59 // (which uses a filter and not the formula.) | 62 // to account for round-off in the actual implementation (which uses a |
| 60 var limitThreshold = 1e-21; | 63 // filter and not the formula.) |
| 64 let limitThreshold = 1e-21; |
| 61 | 65 |
| 62 runTest(should, { | 66 runTest(should, { |
| 63 sampleRate: sampleRate, | 67 sampleRate: sampleRate, |
| 64 v0: 1, | 68 v0: 1, |
| 65 v1: 0, | 69 v1: 0, |
| 66 timeConstant: timeConstant, | 70 timeConstant: timeConstant, |
| 67 eps: limitThreshold, | 71 eps: limitThreshold, |
| 68 // Experimentally determined | 72 // Experimentally determined |
| 69 threshold: 1.3e-7 | 73 threshold: 1.3e-7 |
| 70 }).then(() => task.done()); | 74 }).then(() => task.done()); |
| 71 }); | 75 }); |
| 72 | 76 |
| 73 function findLimitTime(v0, v1, timeConstant, eps) { | 77 function findLimitTime(v0, v1, timeConstant, eps) { |
| 74 // Find the time at which the setTargetAtTime is close enough to the tar
get value |v1| where | 78 // Find the time at which the setTargetAtTime is close enough to the |
| 75 // we can consider the curve to have reached its limiting value. | 79 // target value |v1| where we can consider the curve to have reached its |
| 80 // limiting value. |
| 76 // | 81 // |
| 77 // If v1 = 0, |eps| is the absolute error between the actual value and | 82 // If v1 = 0, |eps| is the absolute error between the actual value and |
| 78 // |v1|. Otherwise, |eps| is the relative error between the actual valu
e and |v1|. | 83 // |v1|. Otherwise, |eps| is the relative error between the actual |
| 84 // value and |v1|. |
| 79 // | 85 // |
| 80 // The curve is | 86 // The curve is |
| 81 // | 87 // |
| 82 // v(t) = v1 - (v1 - v0) * exp(-t/timeConstant) | 88 // v(t) = v1 - (v1 - v0) * exp(-t/timeConstant) |
| 83 // | 89 // |
| 84 // If v1 = 0, | 90 // If v1 = 0, |
| 85 // | 91 // |
| 86 // v(t) = v0 * exp(-t/timeConstant) | 92 // v(t) = v0 * exp(-t/timeConstant) |
| 87 // | 93 // |
| 88 // Solve this for when |v(t)| <= eps: | 94 // Solve this for when |v(t)| <= eps: |
| 89 // | 95 // |
| 90 // t >= timeConstant * log(v0/eps) | 96 // t >= timeConstant * log(v0/eps) |
| 91 // | 97 // |
| 92 // For v1 not zero, we want |v(t) - v1|/|v1| <= eps: | 98 // For v1 not zero, we want |v(t) - v1|/|v1| <= eps: |
| 93 // | 99 // |
| 94 // t >= timeConstant * log(abs(v1-v0)/eps/v1) | 100 // t >= timeConstant * log(abs(v1-v0)/eps/v1) |
| 95 | 101 |
| 96 if (v1) | 102 if (v1) |
| 97 return timeConstant * Math.log(Math.abs(v1-v0)/eps/v1); | 103 return timeConstant * Math.log(Math.abs(v1 - v0) / eps / v1); |
| 98 else | 104 else |
| 99 return timeConstant * Math.log(v0/eps); | 105 return timeConstant * Math.log(v0 / eps); |
| 100 } | 106 } |
| 101 | 107 |
| 102 function runTest(should, options) { | 108 function runTest(should, options) { |
| 103 var renderLength = 1; | 109 let renderLength = 1; |
| 104 | 110 |
| 105 var context = new OfflineAudioContext(1, renderLength * sampleRate, opti
ons.sampleRate); | 111 let context = new OfflineAudioContext( |
| 112 1, renderLength * sampleRate, options.sampleRate); |
| 106 | 113 |
| 107 // A constant source | 114 // A constant source |
| 108 var source = context.createBufferSource(); | 115 let source = context.createBufferSource(); |
| 109 source.buffer = createConstantBuffer(context, 1, 1); | 116 source.buffer = createConstantBuffer(context, 1, 1); |
| 110 source.loop = true; | 117 source.loop = true; |
| 111 | 118 |
| 112 var gain = context.createGain(); | 119 let gain = context.createGain(); |
| 113 gain.gain.setValueAtTime(options.v0, 0); | 120 gain.gain.setValueAtTime(options.v0, 0); |
| 114 gain.gain.setTargetAtTime(options.v1, 0, options.timeConstant); | 121 gain.gain.setTargetAtTime(options.v1, 0, options.timeConstant); |
| 115 | 122 |
| 116 source.connect(gain); | 123 source.connect(gain); |
| 117 gain.connect(context.destination); | 124 gain.connect(context.destination); |
| 118 | 125 |
| 119 source.start(); | 126 source.start(); |
| 120 | 127 |
| 121 return context.startRendering().then(function (resultBuffer) { | 128 return context.startRendering().then(function(resultBuffer) { |
| 122 var actual = resultBuffer.getChannelData(0); | 129 let actual = resultBuffer.getChannelData(0); |
| 123 var expected = createExponentialApproachArray(0, renderLength, | 130 let expected = createExponentialApproachArray( |
| 124 options.v0, options.v1, | 131 0, renderLength, options.v0, options.v1, options.sampleRate, |
| 125 options.sampleRate, options.timeConstant); | 132 options.timeConstant); |
| 126 | 133 |
| 127 var message = "setTargetAtTime(" + options.v1 + ", 0, " + options.time
Constant + ")"; | 134 let message = 'setTargetAtTime(' + options.v1 + ', 0, ' + |
| 135 options.timeConstant + ')'; |
| 128 | 136 |
| 129 // Determine where the tail of the curve begins. (Where the curve has
basically reached | 137 // Determine where the tail of the curve begins. (Where the curve has |
| 130 // the limit value.) | 138 // basically reached the limit value.) |
| 131 var tailTime = findLimitTime(options.v0, options.v1, options.timeConst
ant, options.eps); | 139 let tailTime = findLimitTime( |
| 132 var tailFrame = Math.ceil(tailTime * options.sampleRate); | 140 options.v0, options.v1, options.timeConstant, options.eps); |
| 141 let tailFrame = Math.ceil(tailTime * options.sampleRate); |
| 133 | 142 |
| 134 should(actual.slice(0, tailFrame), | 143 should( |
| 135 "Initial output of " + tailFrame + " samples for " + message) | 144 actual.slice(0, tailFrame), |
| 136 .beCloseToArray(expected.slice(0, tailFrame), { | 145 'Initial output of ' + tailFrame + ' samples for ' + message) |
| 137 absoluteThreshold: options.threshold | 146 .beCloseToArray( |
| 138 }); | 147 expected.slice(0, tailFrame), |
| 148 {absoluteThreshold: options.threshold}); |
| 139 | 149 |
| 140 should(actual.slice(tailFrame), "Tail output for " + message) | 150 should(actual.slice(tailFrame), 'Tail output for ' + message) |
| 141 .containValues([options.v1]); | 151 .containValues([options.v1]); |
| 142 }); | 152 }); |
| 143 } | 153 } |
| 144 | 154 |
| 145 audit.run(); | 155 audit.run(); |
| 146 </script> | 156 </script> |
| 147 </body> | 157 </body> |
| 148 </html> | 158 </html> |
| OLD | NEW |