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 |