OLD | NEW |
---|---|
(Empty) | |
1 <!doctype html> | |
2 <html> | |
3 <head> | |
4 <title>Test Convolver Channel Outputs for Response with 4 channels</title> | |
5 <script src="../../resources/testharness.js"></script> | |
6 <script src="../../resources/testharnessreport.js"></script> | |
7 <script src="../resources/audit-util.js"></script> | |
8 <script src="../resources/audit.js"></script> | |
9 </head> | |
10 | |
11 <body> | |
12 <script> | |
13 // Test various convolver configurations when the convolver response has | |
14 // a four channels. | |
15 // | |
hongchan
2017/03/28 16:44:35
Ditto. Remove the comment tag.
| |
16 // Fairly arbitrary sample rate, except that we want the rate to be a | |
17 // power of two so that 1/sampleRate is exactly respresentable as a | |
18 // single-precision float. | |
19 let sampleRate = 8192; | |
20 | |
21 // A fairly arbitrary number of frames, except the number of frames should | |
22 // be more than a few render quanta. | |
23 let renderFrames = 10 * 128; | |
24 | |
25 let audit = Audit.createTaskRunner(); | |
26 | |
27 // Convolver response | |
28 let response; | |
29 | |
30 audit.define( | |
31 { | |
32 label: 'initialize', | |
33 description: 'Convolver response with one channel' | |
34 }, | |
35 (task, should) => { | |
36 // Convolver response | |
37 should( | |
38 () => { | |
39 response = new AudioBuffer( | |
40 {numberOfChannels: 4, length: 8, sampleRate: sampleRate}); | |
41 // Each channel of the response is a simple impulse (with | |
42 // different delay) so that we can use a DelayNode to simulate | |
43 // the convolver output. Channel k is delayed by k+1 frames. | |
44 for (let k = 0; k < response.numberOfChannels; ++k) { | |
45 response.getChannelData(k)[k + 1] = 1; | |
46 } | |
47 }, | |
48 'new AudioBuffer({numberOfChannels: 2, length: 4, sampleRate: ' + | |
49 sampleRate + '})') | |
50 .notThrow(); | |
51 | |
52 task.done(); | |
53 }); | |
54 | |
55 audit.define( | |
56 {label: '1-channel input', description: 'produces 2-channel output'}, | |
57 (task, should) => { | |
58 fourChannelResponseTest({numberOfInputs: 1, prefix: '1'}, should) | |
59 .then(() => task.done()); | |
60 }); | |
61 | |
62 audit.define( | |
63 {label: '2-channel input', description: 'produces 2-channel output'}, | |
64 (task, should) => { | |
65 fourChannelResponseTest({numberOfInputs: 2, prefix: '2'}, should) | |
66 .then(() => task.done()); | |
67 }); | |
68 | |
69 audit.define( | |
70 { | |
71 label: '3-channel input', | |
72 description: '3->2 downmix producing 2-channel output' | |
73 }, | |
74 (task, should) => { | |
75 fourChannelResponseTest({numberOfInputs: 3, prefix: '3'}, should) | |
76 .then(() => task.done()); | |
77 }); | |
78 | |
79 audit.define( | |
80 { | |
81 label: '4-channel input', | |
82 description: '4->2 downmix producing 2-channel output' | |
83 }, | |
84 (task, should) => { | |
85 fourChannelResponseTest({numberOfInputs: 4, prefix: '4'}, should) | |
86 .then(() => task.done()); | |
87 }); | |
88 | |
89 audit.define( | |
90 { | |
91 label: '5.1-channel input', | |
92 description: '5.1->2 downmix producing 2-channel output' | |
93 }, | |
94 (task, should) => { | |
95 fourChannelResponseTest({numberOfInputs: 6, prefix: '5.1'}, should) | |
96 .then(() => task.done()); | |
97 }); | |
98 | |
99 function fourChannelResponseTest(options, should) { | |
100 // Create an 4-channel offline context. The first two channels are for | |
101 // the stereo output of the convolver and the next two channels are for | |
102 // the reference stereo signal. | |
103 let context = new OfflineAudioContext(4, renderFrames, sampleRate); | |
104 context.destination.channelInterpretation = 'discrete'; | |
105 | |
106 // Create oscillators for use as the input. The type and frequency is | |
107 // arbitrary except that oscillators must be different. | |
108 let src = new Array(options.numberOfInputs); | |
109 for (let k = 0; k < src.length; ++k) { | |
110 src[k] = new OscillatorNode( | |
111 context, {type: 'square', frequency: 440 + 220 * k}); | |
112 } | |
113 | |
114 // Merger to combine the oscillators into one output stream. | |
115 let srcMerger = | |
116 new ChannelMergerNode(context, {numberOfInputs: src.length}); | |
117 | |
118 for (let k = 0; k < src.length; ++k) { | |
119 src[k].connect(srcMerger, 0, k); | |
120 } | |
121 | |
122 // Convolver under test. | |
123 let conv = new ConvolverNode( | |
124 context, {disableNormalization: true, buffer: response}); | |
125 srcMerger.connect(conv); | |
126 | |
127 // Splitter to get individual channels of the convolver output so we can | |
128 // feed them (eventually) to the context in the right set of channels. | |
129 let splitter = new ChannelSplitterNode(context, {numberOfOutputs: 2}); | |
130 conv.connect(splitter); | |
131 | |
132 // Reference graph consists of a delays node to simulate the response of | |
133 // the convolver. (The convolver response is designed this way.) | |
134 let delay = new Array(4); | |
135 for (let k = 0; k < delay.length; ++k) { | |
136 delay[k] = new DelayNode(context, { | |
137 delayTime: (k + 1) / context.sampleRate, | |
138 channelCount: 1, | |
139 channelCountMode: 'explicit' | |
140 }); | |
141 } | |
142 | |
143 | |
hongchan
2017/03/28 16:44:35
Ditto. Two blank lines.
| |
144 // Gain node to mix the sources to stereo in the desired way. (Could be | |
145 // done in the delay node, but let's keep the mixing separated from the | |
146 // functionality.) | |
147 let gainMixer = new GainNode( | |
148 context, {channelCount: 2, channelCountMode: 'explicit'}); | |
149 srcMerger.connect(gainMixer); | |
150 | |
151 // Splitter to extract the channels of the reference signal. | |
152 let refSplitter = | |
153 new ChannelSplitterNode(context, {numberOfOutputs: 2}); | |
154 gainMixer.connect(refSplitter); | |
155 | |
156 // Connect the left channel to the first two nodes and the right channel | |
157 // to the second two as required for "true" stereo matrix response. | |
158 for (let k = 0; k < 2; ++k) { | |
159 refSplitter.connect(delay[k], 0, 0); | |
160 refSplitter.connect(delay[k + 2], 1, 0); | |
161 } | |
162 | |
163 // Gain nodes to sum the responses to stereo | |
164 let gain = new Array(2); | |
165 for (let k = 0; k < gain.length; ++k) { | |
166 gain[k] = new GainNode(context, { | |
167 channelCount: 1, | |
168 channelCountMode: 'explicit', | |
169 channelInterpretation: 'discrete' | |
170 }); | |
171 } | |
172 | |
173 delay[0].connect(gain[0]); | |
174 delay[2].connect(gain[0]); | |
175 delay[1].connect(gain[1]); | |
176 delay[3].connect(gain[1]); | |
177 | |
178 // Final merger to bring back the individual channels from the convolver | |
179 // and the reference in the right order for the destination. | |
180 let finalMerger = new ChannelMergerNode( | |
181 context, {numberOfInputs: context.destination.channelCount}); | |
182 | |
183 // First two channels are for the convolver output, and the next two are | |
184 // for the reference. | |
185 splitter.connect(finalMerger, 0, 0); | |
186 splitter.connect(finalMerger, 1, 1); | |
187 gain[0].connect(finalMerger, 0, 2); | |
188 gain[1].connect(finalMerger, 0, 3); | |
189 | |
190 finalMerger.connect(context.destination); | |
191 | |
192 // Start the sources at last. | |
193 for (let k = 0; k < src.length; ++k) { | |
194 src[k].start(); | |
195 } | |
196 | |
197 return context.startRendering().then(audioBuffer => { | |
198 // Extract the various channels out | |
199 let actual0 = audioBuffer.getChannelData(0); | |
200 let actual1 = audioBuffer.getChannelData(1); | |
201 let expected0 = audioBuffer.getChannelData(2); | |
202 let expected1 = audioBuffer.getChannelData(3); | |
203 | |
204 // Verify that each output channel of the convolver matches | |
205 // the delayed signal from the reference | |
206 should(actual0, options.prefix + ': Channel 0') | |
207 .beEqualToArray(expected0); | |
208 should(actual1, options.prefix + ': Channel 1') | |
209 .beEqualToArray(expected1); | |
210 }); | |
211 } | |
212 | |
213 audit.run(); | |
214 </script> | |
215 </body> | |
216 </html> | |
OLD | NEW |