Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / LayoutTests / webaudio / stereopannernode-no-glitch.html
blobeccf740ea5737f47e70b820943dd3bdebdfa7b02
1 <!DOCTYPE html>
2 <html>
4 <head>
5 <script src="../resources/js-test.js"></script>
6 <script src="resources/compatibility.js"></script>
7 <script src="resources/audio-testing.js"></script>
8 </head>
10 <body>
11 <script>
12 description('Test if StereoPannerNode producing glitches by crossing zero.');
13 window.jsTestIsAsync = true;
15 var audit = Audit.createTaskRunner();
17 var sampleRate = 44100;
19 // Note that this layout test requires rather large render duration because
20 // we need enough time to do something useful in the |onstatechange| event
21 // before rendering finishes.
22 var renderDuration = 20;
24 // The threshold for glitch detection. This was experimentally determined.
25 var GLITCH_THRESHOLD = 0.0005;
27 // The maximum threshold for the error between the actual and the expected
28 // sample values. Experimentally determined.
29 var MAX_ERROR_ALLOWED = 0.0000001;
31 // Option for |Should| test util. The number of array elements to be printed
32 // out is arbitrary.
33 var SHOULD_OPTS = {
34 numberOfArrayLog: 2
38 // Extract a transitional region from the AudioBuffer. If no transition
39 // found, fail this test.
40 function extractPanningTransition(input) {
41 var chanL = input.getChannelData(0);
42 var chanR = input.getChannelData(1);
43 var start, end;
44 var index = 1;
46 // Find transition by comparing two consecutive samples. If two consecutive
47 // samples are identical, the transition has not started.
48 while (chanL[index-1] === chanL[index] || chanR[index-1] === chanR[index]) {
49 if (++index >= input.length) {
50 testFailed('No transition found in the channel data.');
51 return null;
54 start = index - 1;
56 // Find the end of transition. If two consecutive samples are not equal,
57 // the transition is still ongoing.
58 while (chanL[index-1] !== chanL[index] || chanR[index-1] !== chanR[index]) {
59 if (++index >= input.length) {
60 testFailed('A transition found but the buffer ended prematurely.');
61 return null;
64 end = index;
66 testPassed('Transition found. (length = ' + (end - start) + ')');
68 return {
69 left: chanL.subarray(start, end),
70 right: chanR.subarray(start, end),
71 length: end - start
75 // JS implementation of stereo equal power panning.
76 function panStereoEqualPower(pan, inputL, inputR) {
77 pan = Math.min(1.0, Math.max(-1.0, pan));
78 var output = [];
79 var panRadian;
80 if (!inputR) { // mono case.
81 panRadian = (pan * 0.5 + 0.5) * Math.PI / 2;
82 output[0] = inputL * Math.cos(panRadian);
83 output[1] = inputR * Math.sin(panRadian);
84 } else { // stereo case.
85 panRadian = (pan <= 0 ? pan + 1 : pan) * Math.PI / 2;
86 var gainL = Math.cos(panRadian);
87 var gainR = Math.sin(panRadian);
88 if (pan <= 0) {
89 output[0] = inputL + inputR * gainL;
90 output[1] = inputR * gainR;
91 } else {
92 output[0] = inputL * gainL;
93 output[1] = inputR + inputL * gainR;
96 return output;
99 // Generate the expected result of stereo equal panning. |input| is an
100 // AudioBuffer to be panned.
101 function generateStereoEqualPanningResult(input, startPan, endPan, length) {
103 // Smoothing constant time is 0.05 second.
104 var smoothingConstant = 1 - Math.exp(-1 / (sampleRate * 0.05));
106 var inputL = input.getChannelData(0);
107 var inputR = input.getChannelData(1);
108 var pan = startPan;
109 var outputL = [], outputR = [];
111 for (var i = 0; i < length; i++) {
112 var samples = panStereoEqualPower(pan, inputL[i], inputR[i]);
113 outputL[i] = samples[0];
114 outputR[i] = samples[1];
115 pan += (endPan - pan) * smoothingConstant;
118 return {
119 left: outputL,
120 right: outputR
125 function panAndVerify(options, done) {
126 var context = new OfflineAudioContext(2, 44100 * renderDuration, 44100);
127 var source = context.createBufferSource();
128 var panner = context.createStereoPanner();
129 var stereoBuffer = createConstantBuffer(context, 128, [1.0, 1.0]);
131 source.buffer = stereoBuffer;
132 source.loop = true;
134 panner.pan.value = options.startPanValue;
136 source.connect(panner);
137 panner.connect(context.destination);
138 source.start();
140 context.onstatechange = function () {
141 if (context.state === 'running')
142 panner.pan.value = options.endPanValue;
145 context.startRendering().then(function (buffer) {
146 var actual = extractPanningTransition(buffer);
147 var expected = generateStereoEqualPanningResult(stereoBuffer,
148 options.startPanValue, options.endPanValue, actual.length);
150 // |notGlitch| tests are redundant if the actual and expected results
151 // match and if the expected results themselves don't glitch.
152 Should('Channel #0', actual.left).notGlitch(GLITCH_THRESHOLD);
153 Should('Channel #1', actual.right).notGlitch(GLITCH_THRESHOLD);
155 Should('Channel #0', actual.left, SHOULD_OPTS)
156 .beCloseToArray(expected.left, MAX_ERROR_ALLOWED);
157 Should('Channel #1', actual.right, SHOULD_OPTS)
158 .beCloseToArray(expected.right, MAX_ERROR_ALLOWED);
159 }).then(done);
162 // Task: move pan from negative (-0.1) to positive (0.1) value to check if
163 // there is a glitch during the transition. See crbug.com/470559.
164 audit.defineTask('negative-to-positive', function (done) {
165 panAndVerify({ startPanValue: -0.1, endPanValue: 0.1 }, done);
169 // Task: move pan from positive (0.1) to negative (-0.1) value to check if
170 // there is a glitch during the transition.
171 audit.defineTask('positive-to-negative', function (done) {
172 panAndVerify({ startPanValue: 0.1, endPanValue: -0.1 }, done);
175 audit.defineTask('finish-test', function (done) {
176 done();
177 finishJSTest();
180 audit.runTasks(
181 'negative-to-positive',
182 'positive-to-negative',
183 'finish-test'
186 successfullyParsed = true;
187 </script>
188 </body>
190 </html>