| OLD | NEW |
| 1 <!doctype html> | 1 <!DOCTYPE html> |
| 2 <html> | 2 <html> |
| 3 <head> | 3 <head> |
| 4 <title> |
| 5 SetTarget Followed by Linear or Exponential Ramp Is Continuous |
| 6 </title> |
| 4 <script src="../../resources/testharness.js"></script> | 7 <script src="../../resources/testharness.js"></script> |
| 5 <script src="../../resources/testharnessreport.js"></script> | 8 <script src="../../resources/testharnessreport.js"></script> |
| 6 <script src="../resources/audit-util.js"></script> | 9 <script src="../resources/audit-util.js"></script> |
| 7 <script src="../resources/audit.js"></script> | 10 <script src="../resources/audit.js"></script> |
| 8 <script src="../resources/audioparam-testing.js"></script> | 11 <script src="../resources/audioparam-testing.js"></script> |
| 9 <title>SetTarget Followed by Linear or Exponential Ramp Is Continuous</title
> | |
| 10 </head> | 12 </head> |
| 13 <body> |
| 14 <script id="layout-test-code"> |
| 15 let sampleRate = 48000; |
| 16 let renderQuantum = 128; |
| 17 // Test doesn't need to run for very long. |
| 18 let renderDuration = 0.1; |
| 19 // Where the ramp should end |
| 20 let rampEndTime = renderDuration - .05; |
| 21 let renderFrames = renderDuration * sampleRate; |
| 22 let timeConstant = 0.01; |
| 11 | 23 |
| 12 <body> | 24 let audit = Audit.createTaskRunner(); |
| 13 <script> | |
| 14 | 25 |
| 15 var sampleRate = 48000; | 26 // All of the tests start a SetTargetAtTime after one rendering quantum. |
| 16 var renderQuantum = 128; | 27 // The following tests handle various cases where a linear or exponential |
| 17 // Test doesn't need to run for very long. | 28 // ramp is scheduled at or after SetTargetAtTime starts. |
| 18 var renderDuration = 0.1; | |
| 19 // Where the ramp should end | |
| 20 var rampEndTime = renderDuration - .05; | |
| 21 var renderFrames = renderDuration * sampleRate; | |
| 22 var timeConstant = 0.01; | |
| 23 | 29 |
| 24 var audit = Audit.createTaskRunner(); | 30 audit.define('linear ramp replace', (task, should) => { |
| 25 | 31 // Schedule a linear ramp to start at the same time as SetTargetAtTime. |
| 26 // All of the tests start a SetTargetAtTime after one rendering quantum.
The following tests | 32 // This effectively replaces the SetTargetAtTime as if it never existed. |
| 27 // handle various cases where a linear or exponential ramp is scheduled at
or after | 33 runTest(should, 'Linear ramp', { |
| 28 // SetTargetAtTime starts. | 34 automationFunction: function(audioparam, endValue, endTime) { |
| 29 | |
| 30 audit.define("linear ramp replace", (task, should) => { | |
| 31 // Schedule a linear ramp to start at the same time as SetTargetAtTime.
This effectively | |
| 32 // replaces the SetTargetAtTime as if it never existed. | |
| 33 runTest(should, "Linear ramp", { | |
| 34 automationFunction: function (audioparam, endValue, endTime) { | |
| 35 audioparam.linearRampToValueAtTime(endValue, endTime); | 35 audioparam.linearRampToValueAtTime(endValue, endTime); |
| 36 }, | 36 }, |
| 37 referenceFunction: linearResult, | 37 referenceFunction: linearResult, |
| 38 automationTime: renderQuantum / sampleRate, | 38 automationTime: renderQuantum / sampleRate, |
| 39 thresholdSetTarget: 0, | 39 thresholdSetTarget: 0, |
| 40 thresholdRamp: 1.26765e-6 | 40 thresholdRamp: 1.26765e-6 |
| 41 }).then(() => task.done()); | 41 }).then(() => task.done()); |
| 42 }); | 42 }); |
| 43 | 43 |
| 44 audit.define("delayed linear ramp", (task, should) => { | 44 audit.define('delayed linear ramp', (task, should) => { |
| 45 // Schedule a linear ramp to start after the SetTargetAtTime has already
started rendering. | 45 // Schedule a linear ramp to start after the SetTargetAtTime has already |
| 46 // This is the main test to verify that the linear ramp is continuous wi
th the | 46 // started rendering. This is the main test to verify that the linear |
| 47 // SetTargetAtTime curve. | 47 // ramp is continuous with the SetTargetAtTime curve. |
| 48 runTest(should, "Delayed linear ramp", { | 48 runTest(should, 'Delayed linear ramp', { |
| 49 automationFunction: function (audioparam, endValue, endTime) { | 49 automationFunction: function(audioparam, endValue, endTime) { |
| 50 audioparam.linearRampToValueAtTime(endValue, endTime); | 50 audioparam.linearRampToValueAtTime(endValue, endTime); |
| 51 }, | 51 }, |
| 52 referenceFunction: linearResult, | 52 referenceFunction: linearResult, |
| 53 automationTime: 4 * renderQuantum / sampleRate, | 53 automationTime: 4 * renderQuantum / sampleRate, |
| 54 thresholdSetTarget: 3.43632e-7, | 54 thresholdSetTarget: 3.43632e-7, |
| 55 thresholdRamp: 1.07972e-6 | 55 thresholdRamp: 1.07972e-6 |
| 56 }).then(() => task.done()); | 56 }).then(() => task.done()); |
| 57 }); | 57 }); |
| 58 | 58 |
| 59 audit.define("expo ramp replace", (task, should) => { | 59 audit.define('expo ramp replace', (task, should) => { |
| 60 // Like "linear ramp replace", but with an exponential ramp instead. | 60 // Like "linear ramp replace", but with an exponential ramp instead. |
| 61 runTest(should, "Exponential ramp", { | 61 runTest(should, 'Exponential ramp', { |
| 62 automationFunction: function (audioparam, endValue, endTime) { | 62 automationFunction: function(audioparam, endValue, endTime) { |
| 63 audioparam.exponentialRampToValueAtTime(endValue, endTime); | 63 audioparam.exponentialRampToValueAtTime(endValue, endTime); |
| 64 }, | 64 }, |
| 65 referenceFunction: exponentialResult, | 65 referenceFunction: exponentialResult, |
| 66 automationTime: renderQuantum / sampleRate, | 66 automationTime: renderQuantum / sampleRate, |
| 67 thresholdSetTarget: 0, | 67 thresholdSetTarget: 0, |
| 68 thresholdRamp: 1.14441e-5 | 68 thresholdRamp: 1.14441e-5 |
| 69 }).then(() => task.done()); | 69 }).then(() => task.done()); |
| 70 }); | 70 }); |
| 71 | 71 |
| 72 audit.define("delayed expo ramp", (task, should) => { | 72 audit.define('delayed expo ramp', (task, should) => { |
| 73 // Like "delayed linear ramp", but with an exponential ramp instead. | 73 // Like "delayed linear ramp", but with an exponential ramp instead. |
| 74 runTest(should, "Delayed exponential ramp", { | 74 runTest(should, 'Delayed exponential ramp', { |
| 75 automationFunction: function (audioparam, endValue, endTime) { | 75 automationFunction: function(audioparam, endValue, endTime) { |
| 76 audioparam.exponentialRampToValueAtTime(endValue, endTime); | 76 audioparam.exponentialRampToValueAtTime(endValue, endTime); |
| 77 }, | 77 }, |
| 78 referenceFunction: exponentialResult, | 78 referenceFunction: exponentialResult, |
| 79 automationTime: 4 * renderQuantum / sampleRate, | 79 automationTime: 4 * renderQuantum / sampleRate, |
| 80 thresholdSetTarget: 3.43632e-7, | 80 thresholdSetTarget: 3.43632e-7, |
| 81 thresholdRamp: 4.29154e-6 | 81 thresholdRamp: 4.29154e-6 |
| 82 }).then(() => task.done()); | 82 }).then(() => task.done()); |
| 83 }); | 83 }); |
| 84 | 84 |
| 85 audit.run(); | 85 audit.run(); |
| 86 | 86 |
| 87 function computeExpectedResult(automationTime, timeConstant, endValue, end
Time, rampFunction) { | 87 function computeExpectedResult( |
| 88 // The result is a constant value of 1 for one rendering quantum, then a
SetTarget event | 88 automationTime, timeConstant, endValue, endTime, rampFunction) { |
| 89 // lasting to |automationTime|, at which point a ramp starts which ends
at |endValue| | 89 // The result is a constant value of 1 for one rendering quantum, then a |
| 90 // at |endTime|. Then the rest of curve should be held constant at |end
Value|. | 90 // SetTarget event lasting to |automationTime|, at which point a ramp |
| 91 var initialPart = new Array(renderQuantum); | 91 // starts which ends at |endValue| at |endTime|. Then the rest of curve |
| 92 // should be held constant at |endValue|. |
| 93 let initialPart = new Array(renderQuantum); |
| 92 initialPart.fill(1); | 94 initialPart.fill(1); |
| 93 | 95 |
| 94 // Generate 1 extra frame so that we know where to start the linear ramp
. The last sample | 96 // Generate 1 extra frame so that we know where to start the linear |
| 95 // of the array is where the ramp should start from. | 97 // ramp. The last sample of the array is where the ramp should start |
| 96 var setTargetPart = createExponentialApproachArray(renderQuantum / sampl
eRate, | 98 // from. |
| 97 automationTime + 1 / sampleRate, 1, 0, sampleRate, timeConstant); | 99 let setTargetPart = createExponentialApproachArray( |
| 98 var setTargetLength = setTargetPart.length; | 100 renderQuantum / sampleRate, automationTime + 1 / sampleRate, 1, 0, |
| 101 sampleRate, timeConstant); |
| 102 let setTargetLength = setTargetPart.length; |
| 99 | 103 |
| 100 // Generate the ramp starting at |automationTime| with a value from last
value of the | 104 // Generate the ramp starting at |automationTime| with a value from last |
| 101 // SetTarget curve above. | 105 // value of the SetTarget curve above. |
| 102 var rampPart = rampFunction(automationTime, endTime, | 106 let rampPart = rampFunction( |
| 103 setTargetPart[setTargetLength - 1], endValue, sampleRate); | 107 automationTime, endTime, setTargetPart[setTargetLength - 1], |
| 108 endValue, sampleRate); |
| 104 | 109 |
| 105 // Finally finish out the rest with a constant value of |endValue|, if n
eeded. | 110 // Finally finish out the rest with a constant value of |endValue|, if |
| 106 var finalPart = new Array(Math.floor((renderDuration - endTime) * sample
Rate)); | 111 // needed. |
| 112 let finalPart = |
| 113 new Array(Math.floor((renderDuration - endTime) * sampleRate)); |
| 107 finalPart.fill(endValue); | 114 finalPart.fill(endValue); |
| 108 | 115 |
| 109 // Return the four parts separately for testing. | 116 // Return the four parts separately for testing. |
| 110 return { | 117 return { |
| 111 initialPart: initialPart, | 118 initialPart: initialPart, |
| 112 setTargetPart: setTargetPart.slice(0, setTargetLength - 1), | 119 setTargetPart: setTargetPart.slice(0, setTargetLength - 1), |
| 113 rampPart: rampPart, | 120 rampPart: rampPart, |
| 114 tailPart: finalPart | 121 tailPart: finalPart |
| 115 }; | 122 }; |
| 116 } | 123 } |
| 117 | 124 |
| 118 function linearResult(automationTime, timeConstant, endValue, endTime) { | 125 function linearResult(automationTime, timeConstant, endValue, endTime) { |
| 119 return computeExpectedResult(automationTime, timeConstant, endValue, end
Time, createLinearRampArray); | 126 return computeExpectedResult( |
| 127 automationTime, timeConstant, endValue, endTime, |
| 128 createLinearRampArray); |
| 120 } | 129 } |
| 121 | 130 |
| 122 function exponentialResult(automationTime, timeConstant, endValue, endTime
) { | 131 function exponentialResult( |
| 123 return computeExpectedResult(automationTime, timeConstant, endValue, end
Time, createExponentialRampArray); | 132 automationTime, timeConstant, endValue, endTime) { |
| 133 return computeExpectedResult( |
| 134 automationTime, timeConstant, endValue, endTime, |
| 135 createExponentialRampArray); |
| 124 } | 136 } |
| 125 | 137 |
| 126 // Run test to verify that a SetTarget followed by a ramp produces a conti
nuous curve. | 138 // Run test to verify that a SetTarget followed by a ramp produces a |
| 127 // |prefix| is a string to use as a prefix for the messages. |options| is
a dictionary | 139 // continuous curve. |prefix| is a string to use as a prefix for the |
| 128 // describing how the test is run: | 140 // messages. |options| is a dictionary describing how the test is run: |
| 129 // | 141 // |
| 130 // |options.automationFunction| | 142 // |options.automationFunction| |
| 131 // The function to use to start the automation, which should be a line
ar or exponential | 143 // The function to use to start the automation, which should be a |
| 132 // ramp automation. This function has three arguments: | 144 // linear or exponential ramp automation. This function has three |
| 145 // arguments: |
| 133 // audioparam - the AudioParam to be automated | 146 // audioparam - the AudioParam to be automated |
| 134 // endValue - the end value of the ramp | 147 // endValue - the end value of the ramp |
| 135 // endTime - the end time fo the ramp. | 148 // endTime - the end time fo the ramp. |
| 136 // |options.referenceFunction| | 149 // |options.referenceFunction| |
| 137 // The function to generated the expected result. This function has f
our arguments: | 150 // The function to generated the expected result. This function has |
| 151 // four arguments: |
| 138 // automationTime - the value of |options.automationTime| | 152 // automationTime - the value of |options.automationTime| |
| 139 // timeConstant - time constant used for SetTargetAtTime | 153 // timeConstant - time constant used for SetTargetAtTime |
| 140 // rampEndValue - end value for the ramp (same value used for auto
mationFunction) | 154 // rampEndValue - end value for the ramp (same value used for |
| 141 // rampEndTime - end time for the ramp (same value used for autom
ationFunction) | 155 // automationFunction) rampEndTime - end time for the ramp (same |
| 156 // value used for automationFunction) |
| 142 // |options.automationTime| | 157 // |options.automationTime| |
| 143 // Time at which the |automationFunction| is called to start the autom
ation. | 158 // Time at which the |automationFunction| is called to start the |
| 159 // automation. |
| 144 // |options.thresholdSetTarget| | 160 // |options.thresholdSetTarget| |
| 145 // Threshold to use for verifying that the initial (if any) SetTargetA
tTime portion had | 161 // Threshold to use for verifying that the initial (if any) |
| 146 // the correct values. | 162 // SetTargetAtTime portion had the correct values. |
| 147 // |options.thresholdRamp| | 163 // |options.thresholdRamp| |
| 148 // Threshold to use for verifying that the ramp portion had the correc
t values. | 164 // Threshold to use for verifying that the ramp portion had the |
| 165 // correct values. |
| 149 function runTest(should, prefix, options) { | 166 function runTest(should, prefix, options) { |
| 150 var automationFunction = options.automationFunction; | 167 let automationFunction = options.automationFunction; |
| 151 var referenceFunction = options.referenceFunction; | 168 let referenceFunction = options.referenceFunction; |
| 152 var automationTime = options.automationTime; | 169 let automationTime = options.automationTime; |
| 153 var thresholdSetTarget = options.thresholdSetTarget || 0; | 170 let thresholdSetTarget = options.thresholdSetTarget || 0; |
| 154 var thresholdRamp = options.thresholdRamp || 0; | 171 let thresholdRamp = options.thresholdRamp || 0; |
| 155 | 172 |
| 156 // End value for the ramp. Fairly arbitrary, but should be distinctly d
ifferent from the | 173 // End value for the ramp. Fairly arbitrary, but should be distinctly |
| 157 // target value for SetTargetAtTime and the initial value of gain.gain. | 174 // different from the target value for SetTargetAtTime and the initial |
| 158 var rampEndValue = 2; | 175 // value of gain.gain. |
| 159 var context = new OfflineAudioContext(1, renderFrames, sampleRate); | 176 let rampEndValue = 2; |
| 177 let context = new OfflineAudioContext(1, renderFrames, sampleRate); |
| 160 | 178 |
| 161 // A constant source of amplitude 1. | 179 // A constant source of amplitude 1. |
| 162 var source = context.createBufferSource(); | 180 let source = context.createBufferSource(); |
| 163 source.buffer = createConstantBuffer(context, 1, 1); | 181 source.buffer = createConstantBuffer(context, 1, 1); |
| 164 source.loop = true; | 182 source.loop = true; |
| 165 | 183 |
| 166 var gain = context.createGain(); | 184 let gain = context.createGain(); |
| 167 | 185 |
| 168 // The SetTarget starts after one rendering quantum. | 186 // The SetTarget starts after one rendering quantum. |
| 169 gain.gain.setTargetAtTime(0, renderQuantum / context.sampleRate, timeCon
stant); | 187 gain.gain.setTargetAtTime( |
| 188 0, renderQuantum / context.sampleRate, timeConstant); |
| 170 | 189 |
| 171 // Schedule the ramp at |automationTime|. If this time is past the firs
t rendering quantum, | 190 // Schedule the ramp at |automationTime|. If this time is past the |
| 172 // the SetTarget event will run for a bit before running the ramp. Othe
rwise, the SetTarget | 191 // first rendering quantum, the SetTarget event will run for a bit |
| 173 // should be completely replaced by the ramp. | 192 // before running the ramp. Otherwise, the SetTarget should be |
| 174 context.suspend(automationTime) | 193 // completely replaced by the ramp. |
| 175 .then(function () { | 194 context.suspend(automationTime).then(function() { |
| 176 automationFunction(gain.gain, rampEndValue, rampEndTime); | 195 automationFunction(gain.gain, rampEndValue, rampEndTime); |
| 177 context.resume(); | 196 context.resume(); |
| 178 }); | 197 }); |
| 179 | 198 |
| 180 source.connect(gain); | 199 source.connect(gain); |
| 181 gain.connect(context.destination); | 200 gain.connect(context.destination); |
| 182 | 201 |
| 183 source.start(); | 202 source.start(); |
| 184 | 203 |
| 185 return context.startRendering().then(function (resultBuffer) { | 204 return context.startRendering().then(function(resultBuffer) { |
| 186 var success = true; | 205 let success = true; |
| 187 var result = resultBuffer.getChannelData(0); | 206 let result = resultBuffer.getChannelData(0); |
| 188 var expected = referenceFunction(automationTime, timeConstant, rampEnd
Value, rampEndTime); | 207 let expected = referenceFunction( |
| 208 automationTime, timeConstant, rampEndValue, rampEndTime); |
| 189 | 209 |
| 190 // Verify each part of the curve separately. | 210 // Verify each part of the curve separately. |
| 191 var startIndex = 0; | 211 let startIndex = 0; |
| 192 var length = expected.initialPart.length; | 212 let length = expected.initialPart.length; |
| 193 | 213 |
| 194 // Verify that the initial part of the curve is constant. | 214 // Verify that the initial part of the curve is constant. |
| 195 should(result.slice(0, length), prefix + ": Initial part") | 215 should(result.slice(0, length), prefix + ': Initial part') |
| 196 .beCloseToArray(expected.initialPart); | 216 .beCloseToArray(expected.initialPart); |
| 197 | 217 |
| 198 // Verify the SetTarget part of the curve, if the SetTarget did actual
ly run. | 218 // Verify the SetTarget part of the curve, if the SetTarget did |
| 219 // actually run. |
| 199 startIndex += length; | 220 startIndex += length; |
| 200 length = expected.setTargetPart.length; | 221 length = expected.setTargetPart.length; |
| 201 if (length) { | 222 if (length) { |
| 202 should(result.slice(startIndex, startIndex + length), | 223 should( |
| 203 prefix + ": SetTarget part") | 224 result.slice(startIndex, startIndex + length), |
| 204 .beCloseToArray(expected.setTargetPart, { | 225 prefix + ': SetTarget part') |
| 205 absoluteThreshold: thresholdSetTarget | 226 .beCloseToArray( |
| 206 }); | 227 expected.setTargetPart, |
| 228 {absoluteThreshold: thresholdSetTarget}); |
| 207 } else { | 229 } else { |
| 208 should(!length, prefix + ": SetTarget part") | 230 should(!length, prefix + ': SetTarget part') |
| 209 .message("was correctly replaced by the ramp", | 231 .message( |
| 210 "was incorrectly replaced by the ramp"); | 232 'was correctly replaced by the ramp', |
| 233 'was incorrectly replaced by the ramp'); |
| 211 } | 234 } |
| 212 | 235 |
| 213 // Verify the ramp part of the curve | 236 // Verify the ramp part of the curve |
| 214 startIndex += length; | 237 startIndex += length; |
| 215 length = expected.rampPart.length; | 238 length = expected.rampPart.length; |
| 216 should(result.slice(startIndex, startIndex + length), prefix) | 239 should(result.slice(startIndex, startIndex + length), prefix) |
| 217 .beCloseToArray(expected.rampPart, { | 240 .beCloseToArray( |
| 218 absoluteThreshold: thresholdRamp | 241 expected.rampPart, {absoluteThreshold: thresholdRamp}); |
| 219 }); | |
| 220 | 242 |
| 221 // Verify that the end of the curve after the ramp (if any) is a const
ant. | 243 // Verify that the end of the curve after the ramp (if any) is a |
| 244 // constant. |
| 222 startIndex += length; | 245 startIndex += length; |
| 223 should(result.slice(startIndex), prefix + ": Tail part") | 246 should(result.slice(startIndex), prefix + ': Tail part') |
| 224 .beCloseToArray(expected.tailPart); | 247 .beCloseToArray(expected.tailPart); |
| 225 | 248 |
| 226 }); | 249 }); |
| 227 } | 250 } |
| 228 </script> | 251 </script> |
| 229 </body> | 252 </body> |
| 230 </html> | 253 </html> |
| OLD | NEW |