4 <title>Test Interpolation for AudioParam.setValueCurveAtTime
</title>
5 <script src=
"../resources/js-test.js"></script>
6 <script src=
"resources/compatibility.js"></script>
7 <script src=
"resources/audio-testing.js"></script>
12 description("Test Interpolation for AudioParam.setValueCurveAtTime");
13 window
.jsTestIsAsync
= true;
15 // Play a constant signal through a gain node that is automated using setValueCurveAtTime with
16 // a 2-element curve. The output should be a linear change.
18 // Choose a sample rate that is a multiple of 128, the rendering quantum size. This makes the
19 // math work out to be nice numbers.
20 var sampleRate
= 25600;
21 var testDurationSec
= 1;
22 var testDurationFrames
= testDurationSec
* sampleRate
;
24 // Where the curve starts and its duration. This MUST be less than the total rendering time.
25 var curveStartTime
= 256 / sampleRate
;
26 var curveDuration
= 300 / sampleRate
;;
27 var curveValue
= 0.75;
29 // At this time, the gain node goes to gain 1. This is used to make sure the value curve is
30 // propagated correctly until the next event.
31 var fullGainTime
= 0.75;
33 // Thresholds use to determine if the test passes; these are experimentally determined. The
34 // SNR between the actual and expected result should be at least |snrThreshold|. The maximum
35 // difference betwen them should not exceed |maxErrorThreshold|.
36 var snrThreshold
= 10000;
37 var maxErrorThreshold
= 0;
43 var audit
= Audit
.createTaskRunner();
45 // Array of test configs. Each config must specify curveStartTime, curveDuration,
46 // curveLength, fullGainTime, maxErrorThreshold, and snrThreshold.
49 curveStartTime
: 256 / sampleRate
,
50 curveDuration
: 300 / sampleRate
,
56 // Increase the curve length
57 curveStartTime
: 256 / sampleRate
,
58 curveDuration
: 300 / sampleRate
,
64 // Increase the curve length
65 curveStartTime
: 256 / sampleRate
,
66 curveDuration
: 300 / sampleRate
,
69 maxErrorThreshold
: 5.961e-8,
72 // Increase the curve length
73 curveStartTime
: 256 / sampleRate
,
74 curveDuration
: 300 / sampleRate
,
77 maxErrorThreshold
: 5.961e-8,
80 // Corner case with duration less than a frame!
81 curveStartTime
: 256 / sampleRate
,
82 curveDuration
: 0.25 / sampleRate
,
88 // Short duration test
89 curveStartTime
: 256 / sampleRate
,
90 curveDuration
: 2 / sampleRate
,
96 // Short duration test with many points.
97 curveStartTime
: 256 / sampleRate
,
98 curveDuration
: 2 / sampleRate
,
101 maxErrorThreshold
: 0,
104 // Long duration, big curve
105 curveStartTime
: 256 / sampleRate
,
109 maxErrorThreshold
: 5.961e-8,
110 snrThreshold
: 155.310
113 // Creates a function based on the test config that is suitable for use by defineTask().
114 function createTaskFunction(config
) {
115 return function (done
) {
116 runTest(config
).then(done
);
120 // Define a task for each config, in the order listed in testConfigs.
121 for (var k
= 0; k
< testConfigs
.length
; ++k
) {
122 var config
= testConfigs
[k
];
123 var name
= k
+ ":curve=" + config
.curveLength
+ ",duration=" + (config
.curveDuration
* sampleRate
);
124 audit
.defineTask(name
, createTaskFunction(config
));
127 // Must be the last defined task.
128 audit
.defineTask("end", function (done
) {
133 function runTest(config
) {
134 context
= new OfflineAudioContext(1, testDurationFrames
, sampleRate
);
136 // A constant audio source of value 1.
137 var source
= context
.createBufferSource();
138 source
.buffer
= createConstantBuffer(context
, 1, 1);
141 // The value curve for testing. Just to make things easy for testing, make the curve a
142 // simple ramp up to curveValue.
143 // TODO(rtoy): Maybe allow more complicated curves?
144 var curve
= new Float32Array(config
.curveLength
);
145 for (var k
= 0; k
< config
.curveLength
; ++k
) {
146 curve
[k
] = curveValue
/ (config
.curveLength
- 1) * k
;
149 // A gain node that is to be automated using setValueCurveAtTime.
150 var gain
= context
.createGain();
152 gain
.gain
.setValueCurveAtTime(curve
, config
.curveStartTime
, config
.curveDuration
);
153 // This is to verify that setValueCurveAtTime ends appropriately.
154 gain
.gain
.setValueAtTime(1, config
.fullGainTime
);
156 source
.connect(gain
);
157 gain
.connect(context
.destination
);
160 // Some consistency checks on the test parameters
161 Should("Check: Curve end time", config
.curveStartTime
+ config
.curveDuration
)
162 .beLessThanOrEqualTo(testDurationSec
);
163 Should("Check: Full gain start time", config
.fullGainTime
).beLessThanOrEqualTo(testDurationSec
);
164 Should("Check: Full gain start time", config
.fullGainTime
).beGreaterThanOrEqualTo(config
.curveStartTime
+ config
.curveDuration
);
167 return context
.startRendering().then(checkResult(config
));
170 // Return a function to check that the rendered result matches the expected result.
171 function checkResult(config
) {
172 return function (renderedBuffer
) {
175 actualResult
= renderedBuffer
.getChannelData(0);
176 expectedResult
= computeExpectedResult(config
);
178 // Compute the SNR and max absolute difference between the actual and expected result.
179 var SNR
= 10*Math
.log10(computeSNR(actualResult
, expectedResult
));
183 for (var k
= 0; k
< actualResult
.length
; ++k
) {
184 var diff
= Math
.abs(actualResult
[k
] - expectedResult
[k
]);
185 if (maxDiff
< diff
) {
191 success
= success
&& Should("SNR", SNR
).beGreaterThanOrEqualTo(config
.snrThreshold
);
193 if (maxDiff
<= config
.maxErrorThreshold
) {
194 testPassed("Max difference is less than or equal to " + config
.maxErrorThreshold
+ ".");
196 testFailed("Max difference (" + maxDiff
+ ") NOT less than or equal to " +
197 config
.maxErrorThreshold
+ " at frame " + posn
+ ".");
201 var message
= "Test: curve length = " + config
.curveLength
+ "; duration frames = " +
202 config
.curveDuration
* sampleRate
+ ".\n";
211 // Compute the expected result based on the config settings.
212 function computeExpectedResult(config
) {
213 // The automation curve starts at |curveStartTime| and has duration |curveDuration|. So,
214 // the output should be zero until curveStartTime, linearly ramp up from there to
215 // |curveValue|, and then be constant 1 from then to the end of the buffer.
217 var expected
= new Float32Array(testDurationFrames
);
219 var curveStartFrame
= config
.curveStartTime
* sampleRate
;
220 var curveEndFrame
= Math
.floor((config
.curveStartTime
+ config
.curveDuration
) * sampleRate
);
221 var fullGainFrame
= config
.fullGainTime
* sampleRate
;
225 // Zero out the start.
226 for (k
= 0; k
< curveStartFrame
; ++k
)
229 // Linearly ramp now. This assumes that the actual curve used is a linear ramp, even if
230 // there are many curve points.
231 var stepSize
= curveValue
/ (config
.curveDuration
* sampleRate
- 1);
232 for (; k
< curveEndFrame
; ++k
)
233 expected
[k
] = stepSize
* (k
- curveStartFrame
);
235 // Hold it constant until the next event
236 for (; k
< fullGainFrame
; ++k
)
237 expected
[k
] = curveValue
;
239 // Amplitude is one for the rest of the test.
240 for (; k
< testDurationFrames
; ++k
)
248 successfullyParsed
= true;