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