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