summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webaudio/the-audio-api/the-waveshapernode-interface/curve-tests.html
blob: 791b74a6c08a4936c545d8ebb330ee01ed496901 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
<!doctype html>
<html>
<head>
	<title>WaveShaperNode interface - Curve tests | WebAudio</title>

	<script type="text/javascript" src="/resources/testharness.js"></script>
	<script type="text/javascript" src="/resources/testharnessreport.js"></script>
	<script type="text/javascript" src="../../js/vendor-prefixes.js"></script>
</head>
<body>
	<div id="log">
	</div>

	<script type="text/javascript">
		var sampleRate=44100.0;
		var tolerance=0.01;

		/*
		Testing that -1, 0 and +1 map correctly to curve (with 1:1 correlation)
		=======================================================================
		From the specification:
			The input signal is nominally within the range -1 -> +1.
			Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
				to the center value of the curve array.
		*/
		(function() {
			var threeElementCurve=[2.0, -3.0, 4.0];
			var inputData=[-1.0, 0, 1.0];
			var expectedData=[2.0, -3.0, 4.0];
			executeTest(threeElementCurve, inputData, expectedData, "Testing that -1, 0 and +1 map correctly to curve (with 1:1 correlation)");
		})();

		/*
		Testing interpolation (where inputs don't correlate directly to curve elements)
		===============================================================================
		From the specification:
			The implementation must perform linear interpolation between adjacent points in the curve.
		*/
		(function() {
			var threeElementCurve=[2.0, -3.0, 4.0];
			var inputData=[-0.5, +0.5, +0.75];
			var expectedData=[-0.5, +0.5, +2.25];
			executeTest(threeElementCurve, inputData, expectedData, "Testing interpolation (where inputs don't correlate directly to curve elements)");
		})();

		/*
		Testing out-of-range inputs (should be mapped to the first/last elements of the curve)
		======================================================================================
		From the specification:
			Any sample value less than -1 will correspond to the first value in the curve array.
			Any sample value greater than +1 will correspond to the last value in the curve array.
		*/
		(function() {
			var threeElementCurve=[2.0, -3.0, 4.0];
			var inputData=[-1.5, +1.5];
			var expectedData=[2.0, 4.0];
			executeTest(threeElementCurve, inputData, expectedData, "Testing out-of-range inputs (should be mapped to the first/last elements of the curve)");
		})();

		/*
		Testing a 2-element curve (does not have a middle element)
		==========================================================
		From the specification:
			Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
				to the center value of the curve array.
			The implementation must perform linear interpolation between adjacent points in the curve.
		*/
		(function() {
			var twoElementCurve=[2.0, -2.0];
			var inputData=[-1.0, 0, 1.0];
			var expectedData=[2.0, 0.0, -2.0];
			executeTest(twoElementCurve, inputData, expectedData, "Testing a 2-element curve (does not have a middle element)");
		})();

		/*
		Testing a 4-element curve (does not have a middle element)
		==========================================================
		From the specification:
			Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
				to the center value of the curve array.
			The implementation must perform linear interpolation between adjacent points in the curve.
		*/
		(function() {
			var fourElementCurve=[1.0, 2.0, 4.0, 7.0];
			var inputData=[-1.0, 0, 1.0];
			var expectedData=[1.0, 3.0, 7.0];
			executeTest(fourElementCurve, inputData, expectedData, "Testing a 4-element curve (does not have a middle element)");
		})();

		/*
		Testing a huge curve
		====================
		From the specification:
			Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
				to the center value of the curve array.
		*/
		(function() {
			var bigCurve=[];
			for(var i=0;i<=60000;i++) { bigCurve.push(i/3.5435); }
			var inputData=[-1.0, 0, 1.0];
			var expectedData=[bigCurve[0], bigCurve[30000], bigCurve[60000]];
			executeTest(bigCurve, inputData, expectedData, "Testing a huge curve");
		})();

		/*
		Testing single-element curve (boundary condition)
		=================================================
		From the specification:
			Each input sample within this range will index into the shaping curve with a signal level of zero corresponding
				to the center value of the curve array.
			Any sample value less than -1 will correspond to the first value in the curve array.
			Any sample value greater than +1 will correspond to the last value in the curve array.
			The implementation must perform linear interpolation between adjacent points in the curve.
		Note:
			I found a post on the W3C audio mailing list (from one of the Chris's) that suggested it would be feasible
				to use the WaveShaperNode to create constant values.
		*/
		(function() {
			var oneElementCurve=[1.0];
			var inputData=[-1.0, 0, 1.0, -2.0, 2.0];
			var expectedData=[1.0, 1.0, 1.0, 1.0, 1.0];
			executeTest(oneElementCurve, inputData, expectedData, "Testing single-element curve (boundary condition)");
		})();

		/*
		Testing null curve (should return input values)
		===============================================
		From the specification:
			Initially the curve attribute is null, which means that the WaveShaperNode will pass its input to its output
				without modification.
		*/
		(function() {
			var inputData=[-1.0, 0, 1.0, 2.0];
			var expectedData=[-1.0, 0.0, 1.0, 2.0];
			executeTest(null, inputData, expectedData, "Testing null curve (should return input values)");
		})();

		/*
		Testing zero-element curve (unspecified result)
		===============================================
		From the specification:
			Unspecified result (I assume it will be treated in the same way as a null curve).
		Note:
			Mozilla test_waveShaperNoCurve.html indicates they expect same results as a null curve.
		*/
		(function() {
			var zeroElementCurve=[];
			var inputData=[-1.0, 0, 1.0, 2.0];
			var expectedData=[-1.0, 0.0, 1.0, 2.0];
			executeTest(zeroElementCurve, inputData, expectedData, "Testing zero-element curve (unspecified result)");
		})();


		/**
		* Function that does the actual testing (using an asynchronous test).
		* @param {?Array.<number>} curveData - Array containing values for the WaveShaper curve.
		* @param {!Array.<number>} inputData - Array containing values for the input stream.
		* @param {!Array.<number>} expectedData - Array containing expected results for each of the corresponding inputs.
		* @param {!string} testName - Name of the test case.
		*/
		function executeTest(curveData, inputData, expectedData, testName) {
			var stTest=async_test("WaveShaperNode - "+testName);

			// Create offline audio context.
			var ac=new OfflineAudioContext(1, inputData.length, sampleRate);

			// Create the WaveShaper and its curve.
			var waveShaper=ac.createWaveShaper();
			if(curveData!=null) {
				var curve=new Float32Array(curveData.length);
				for(var i=0;i<curveData.length;i++) { curve[i]=curveData[i]; }
				waveShaper.curve=curve;
			}
			waveShaper.connect(ac.destination);

			// Create buffer containing the input values.
			var inputBuffer=ac.createBuffer(1, Math.max(inputData.length, 2), sampleRate);
			var d=inputBuffer.getChannelData(0);
			for(var i=0;i<inputData.length;i++) { d[i]=inputData[i]; }

			// Play the input buffer through the WaveShaper.
			var src=ac.createBufferSource();
			src.buffer=inputBuffer;
			src.connect(waveShaper);
			src.start();

			// Test the outputs match the expected values.
			ac.oncomplete=function(ev) {
				var d=ev.renderedBuffer.getChannelData(0);

				stTest.step(function() {
					for(var i=0;i<expectedData.length;i++) {
						var curveText="null";
						if(curve!=null) {
							if(curveData.length<20) {
								curveText=curveData.join(",");
							} else {
								curveText="TooBigToDisplay ("+(curveData.length-1)+" elements)";
							}
						}
						var comment="Input="+inputData[i]+", Curve=["+curveText+"] >>> ";
						assert_approx_equals(d[i], expectedData[i], tolerance, comment);
					}
				});

				stTest.done();
			};
			ac.startRendering();
		}
	</script>
</body>
</html>