Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(297)

Side by Side Diff: third_party/WebKit/LayoutTests/webaudio/panner-automation-position.html

Issue 1820403002: Implement Automations for PannerNode and AutioListener (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Update test because CL for min/maxValue AudioParam landed Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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/panner-formulas.js"></script>
8 <title>Test Automation of PannerNode Positions</title>
9 </head>
10
11 <body>
12 <script>
13 description("Test Automation of PannerNode Positions.");
14 window.jsTestIsAsync = true;
15
16 var sampleRate = 48000;
17 // These tests are quite slow, so don't run for many frames. 256 frames s hould be enough to
18 // demonstrate that automations are working.
19 var renderFrames = 256;
20 var renderDuration = renderFrames / sampleRate;
21
22 var context;
23 var panner;
24
25 var audit = Audit.createTaskRunner();
26
27 // Set of tests for the panner node with automations applied to the positi on of the source.
28 var testConfigs = [{
29 // Distance model parameters for the panner
30 distanceModel: {
31 model: "inverse",
32 rolloff: 1
33 },
34 // Initial location of the source
35 startPosition: [0, 0, 1],
36 // Final position of the source. For this test, we only want to move on the z axis which
37 // doesn't change the azimuth angle.
38 endPosition: [0, 0, 10000],
39 }, {
40 distanceModel: {
41 model: "inverse",
42 rolloff: 1
43 },
44 startPosition: [0, 0, 1],
45 // An essentially random end position, but it should be such that azimut h angle changes as
46 // we move from the start to the end.
47 endPosition: [20000, 30000, 10000],
48 errorThreshold: [{
49 // Error threshold for 1-channel case
50 relativeThreshold: 4.8124e-7
51 }, {
52 // Error threshold for 2-channel case
53 relativeThreshold: 4.3267e-7
54 }],
55 }, {
56 distanceModel: {
57 model: "exponential",
58 rolloff: 1.5
59 },
60 startPosition: [0, 0, 1],
61 endPosition: [20000, 30000, 10000],
62 errorThreshold: [{
63 relativeThreshold: 5.0783e-7
64 }, {
65 relativeThreshold: 5.2180e-7
66 }]
67 }, {
68 distanceModel: {
69 model: "linear",
70 rolloff: 1
71 },
72 startPosition: [0, 0, 1],
73 endPosition: [20000, 30000, 10000],
74 errorThreshold: [{
75 relativeThreshold: 6.5324e-6
76 }, {
77 relativeThreshold: 6.5756e-6
78 }]
79 }];
80
81 for (var k = 0; k < testConfigs.length; ++k) {
82 var config = testConfigs[k];
83 var tester = function (c, channelCount) {
84 return function (done) {
85 runTest(c, channelCount).then(done);
86 }
87 };
88
89 var baseTestName = config.distanceModel.model + " rolloff: " + config.di stanceModel.rolloff;
90
91 // Define tasks for both 1-channel and 2-channel
92 audit.defineTask(k + ": 1-channel " + baseTestName, tester(config, 1));
93 audit.defineTask(k + ": 2-channel " + baseTestName, tester(config, 2));
94 }
95
96 audit.defineTask("finish", function (done) {
97 finishJSTest();
98 done();
99 });
100
101 audit.runTasks();
102
103 function runTest(options, channelCount) {
104 // Output has 5 channels: channels 0 and 1 are for the stereo output of the panner node.
105 // Channels 2-5 are the for automation of the x,y,z coordinate so that w e have actual
106 // coordinates used for the panner automation.
107 context = new OfflineAudioContext(5, renderFrames, sampleRate);
108
109 // Stereo source for the panner.
110 var source = context.createBufferSource();
111 source.buffer = createConstantBuffer(context, renderFrames, channelCount == 1 ? 1 : [1, 2]);
112
113 panner = context.createPanner();
114 panner.distanceModel = options.distanceModel.model;
115 panner.rolloffFactor = options.distanceModel.rolloff;
116 panner.panningModel = "equalpower";
117
118 // Source and gain node for the z-coordinate calculation.
119 var dist = context.createBufferSource();
120 dist.buffer = createConstantBuffer(context, 1, 1);
121 dist.loop = true;
122 var gainX = context.createGain();
123 var gainY = context.createGain();
124 var gainZ = context.createGain();
125 dist.connect(gainX);
126 dist.connect(gainY);
127 dist.connect(gainZ);
128
129 // Set the gain automation to match the z-coordinate automation of the p anner.
130
131 // End the automation some time before the end of the rendering so we ca n verify that
132 // automation has the correct end time and value.
133 var endAutomationTime = 0.75 * renderDuration;
134
135 gainX.gain.setValueAtTime(options.startPosition[0], 0);
136 gainX.gain.linearRampToValueAtTime(options.endPosition[0], endAutomation Time);
137 gainY.gain.setValueAtTime(options.startPosition[1], 0);
138 gainY.gain.linearRampToValueAtTime(options.endPosition[1], endAutomation Time);
139 gainZ.gain.setValueAtTime(options.startPosition[2], 0);
140 gainZ.gain.linearRampToValueAtTime(options.endPosition[2], endAutomation Time);
141
142 dist.start();
143
144 // Splitter and merger to map the panner output and the z-coordinate aut omation to the
145 // correct channels in the destination.
146 var splitter = context.createChannelSplitter(2);
147 var merger = context.createChannelMerger(5);
148
149 source.connect(panner);
150 // Split the output of the panner to separate channels
151 panner.connect(splitter);
152
153 // Merge the panner outputs and the z-coordinate output to the correct d estination channels.
154 splitter.connect(merger, 0, 0);
155 splitter.connect(merger, 1, 1);
156 gainX.connect(merger, 0, 2);
157 gainY.connect(merger, 0, 3);
158 gainZ.connect(merger, 0, 4);
159
160 merger.connect(context.destination);
161
162 // Initialize starting point of the panner.
163 panner.positionX.setValueAtTime(options.startPosition[0], 0);
164 panner.positionY.setValueAtTime(options.startPosition[1], 0);
165 panner.positionZ.setValueAtTime(options.startPosition[2], 0);
166
167 // Automate z coordinate to move away from the listener
168 panner.positionX.linearRampToValueAtTime(options.endPosition[0], 0.75 * renderDuration);
169 panner.positionY.linearRampToValueAtTime(options.endPosition[1], 0.75 * renderDuration);
170 panner.positionZ.linearRampToValueAtTime(options.endPosition[2], 0.75 * renderDuration);
171
172 source.start();
173
174 // Go!
175 return context.startRendering()
176 .then(function (renderedBuffer) {
177 // Get the panner outputs
178 var data0 = renderedBuffer.getChannelData(0);
179 var data1 = renderedBuffer.getChannelData(1);
180 var xcoord = renderedBuffer.getChannelData(2);
181 var ycoord = renderedBuffer.getChannelData(3);
182 var zcoord = renderedBuffer.getChannelData(4);
183
184 // We're doing a linear ramp on the Z axis with the equalpower panne r, so the equalpower
185 // panning gain remains constant. We only need to model the distanc e effect.
186
187 // Compute the distance gain
188 var distanceGain = new Float32Array(xcoord.length);;
189
190 if (panner.distanceModel === "inverse") {
191 for (var k = 0; k < distanceGain.length; ++k) {
192 distanceGain[k] = inverseDistance(panner, xcoord[k], ycoord[k], zcoord[k])
193 }
194 } else if (panner.distanceModel === "linear") {
195 for (var k = 0; k < distanceGain.length; ++k) {
196 distanceGain[k] = linearDistance(panner, xcoord[k], ycoord[k], z coord[k])
197 }
198 } else if (panner.distanceModel === "exponential") {
199 for (var k = 0; k < distanceGain.length; ++k) {
200 distanceGain[k] = exponentialDistance(panner, xcoord[k], ycoord[ k], zcoord[k])
201 }
202 }
203
204 // Compute the expected result. Since we're on the z-axis, the left and right channels
205 // pass through the equalpower panner unchanged. Only need to apply the distance gain.
206 var buffer0 = source.buffer.getChannelData(0);
207 var buffer1 = channelCount == 2 ? source.buffer.getChannelData(1) : buffer0;
208
209 var azimuth = new Float32Array(buffer0.length);
210
211 for (var k = 0; k < data0.length; ++k) {
212 azimuth[k] = calculateAzimuth([
213 xcoord[k],
214 ycoord[k],
215 zcoord[k]
216 ], [
217 context.listener.positionX.value,
218 context.listener.positionY.value,
219 context.listener.positionZ.value
220 ], [
221 context.listener.forwardX.value,
222 context.listener.forwardY.value,
223 context.listener.forwardZ.value
224 ], [
225 context.listener.upX.value,
226 context.listener.upY.value,
227 context.listener.upZ.value
228 ]);
229 }
230
231 var expected = applyPanner(azimuth, buffer0, buffer1, channelCount);
232 var expected0 = expected.left;
233 var expected1 = expected.right;
234
235 for (var k = 0; k < expected0.length; ++k) {
236 expected0[k] *= distanceGain[k];
237 expected1[k] *= distanceGain[k];
238 }
239
240 var info = options.distanceModel.model + ", rolloff: " + options.dis tanceModel.rolloff;
241 var prefix = channelCount + "-channel "
242 + "[" + options.startPosition[0] + ", "
243 + options.startPosition[1] + ", "
244 + options.startPosition[2] + "] -> ["
245 + options.endPosition[0] + ", "
246 + options.endPosition[1] + ", "
247 + options.endPosition[2] + "]: ";
248
249 var errorThreshold = 0;
250
251 if (options.errorThreshold)
252 errorThreshold = options.errorThreshold[channelCount - 1]
253
254 Should(prefix + "distanceModel: " + info + ", left channel", data0, {
255 verbose: true
256 })
257 .beCloseToArray(expected0, errorThreshold);
258 Should(prefix + "distanceModel: " + info + ", right channel", data1, {
259 verbose: true
260 })
261 .beCloseToArray(expected1, errorThreshold);
262 });
263 }
264 </script>
265 </body>
266 </html>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698