OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include <memory> | |
12 | |
13 #include "webrtc/modules/audio_processing/utility/blocker.h" | |
14 | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 #include "webrtc/base/arraysize.h" | |
17 | |
18 namespace { | |
19 | |
20 // Callback Function to add 3 to every sample in the signal. | |
21 class PlusThreeBlockerCallback : public webrtc::BlockerCallback { | |
22 public: | |
23 void ProcessBlock(const float* const* input, | |
24 size_t num_frames, | |
25 size_t num_input_channels, | |
26 size_t num_output_channels, | |
27 float* const* output) override { | |
28 for (size_t i = 0; i < num_output_channels; ++i) { | |
29 for (size_t j = 0; j < num_frames; ++j) { | |
30 output[i][j] = input[i][j] + 3; | |
31 } | |
32 } | |
33 } | |
34 }; | |
35 | |
36 // No-op Callback Function. | |
37 class CopyBlockerCallback : public webrtc::BlockerCallback { | |
38 public: | |
39 void ProcessBlock(const float* const* input, | |
40 size_t num_frames, | |
41 size_t num_input_channels, | |
42 size_t num_output_channels, | |
43 float* const* output) override { | |
44 for (size_t i = 0; i < num_output_channels; ++i) { | |
45 for (size_t j = 0; j < num_frames; ++j) { | |
46 output[i][j] = input[i][j]; | |
47 } | |
48 } | |
49 } | |
50 }; | |
51 | |
52 } // namespace | |
53 | |
54 namespace webrtc { | |
55 | |
56 // Tests blocking with a window that multiplies the signal by 2, a callback | |
57 // that adds 3 to each sample in the signal, and different combinations of chunk | |
58 // size, block size, and shift amount. | |
59 class BlockerTest : public ::testing::Test { | |
60 protected: | |
61 void RunTest(Blocker* blocker, | |
62 size_t chunk_size, | |
63 size_t num_frames, | |
64 const float* const* input, | |
65 float* const* input_chunk, | |
66 float* const* output, | |
67 float* const* output_chunk, | |
68 size_t num_input_channels, | |
69 size_t num_output_channels) { | |
70 size_t start = 0; | |
71 size_t end = chunk_size - 1; | |
72 while (end < num_frames) { | |
73 CopyTo(input_chunk, 0, start, num_input_channels, chunk_size, input); | |
74 blocker->ProcessChunk(input_chunk, | |
75 chunk_size, | |
76 num_input_channels, | |
77 num_output_channels, | |
78 output_chunk); | |
79 CopyTo(output, start, 0, num_output_channels, chunk_size, output_chunk); | |
80 | |
81 start += chunk_size; | |
82 end += chunk_size; | |
83 } | |
84 } | |
85 | |
86 void ValidateSignalEquality(const float* const* expected, | |
87 const float* const* actual, | |
88 size_t num_channels, | |
89 size_t num_frames) { | |
90 for (size_t i = 0; i < num_channels; ++i) { | |
91 for (size_t j = 0; j < num_frames; ++j) { | |
92 EXPECT_FLOAT_EQ(expected[i][j], actual[i][j]); | |
93 } | |
94 } | |
95 } | |
96 | |
97 void ValidateInitialDelay(const float* const* output, | |
98 size_t num_channels, | |
99 size_t num_frames, | |
100 size_t initial_delay) { | |
101 for (size_t i = 0; i < num_channels; ++i) { | |
102 for (size_t j = 0; j < num_frames; ++j) { | |
103 if (j < initial_delay) { | |
104 EXPECT_FLOAT_EQ(output[i][j], 0.f); | |
105 } else { | |
106 EXPECT_GT(output[i][j], 0.f); | |
107 } | |
108 } | |
109 } | |
110 } | |
111 | |
112 static void CopyTo(float* const* dst, | |
113 size_t start_index_dst, | |
114 size_t start_index_src, | |
115 size_t num_channels, | |
116 size_t num_frames, | |
117 const float* const* src) { | |
118 for (size_t i = 0; i < num_channels; ++i) { | |
119 memcpy(&dst[i][start_index_dst], | |
120 &src[i][start_index_src], | |
121 num_frames * sizeof(float)); | |
122 } | |
123 } | |
124 }; | |
125 | |
126 TEST_F(BlockerTest, TestBlockerMutuallyPrimeChunkandBlockSize) { | |
127 const size_t kNumInputChannels = 3; | |
128 const size_t kNumOutputChannels = 2; | |
129 const size_t kNumFrames = 10; | |
130 const size_t kBlockSize = 4; | |
131 const size_t kChunkSize = 5; | |
132 const size_t kShiftAmount = 2; | |
133 | |
134 const float kInput[kNumInputChannels][kNumFrames] = { | |
135 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, | |
136 {2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, | |
137 {3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; | |
138 ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels); | |
139 input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput)); | |
140 | |
141 const float kExpectedOutput[kNumInputChannels][kNumFrames] = { | |
142 {6, 6, 12, 20, 20, 20, 20, 20, 20, 20}, | |
143 {6, 6, 12, 28, 28, 28, 28, 28, 28, 28}}; | |
144 ChannelBuffer<float> expected_output_cb(kNumFrames, kNumInputChannels); | |
145 expected_output_cb.SetDataForTesting( | |
146 kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput)); | |
147 | |
148 const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; | |
149 | |
150 ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels); | |
151 ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels); | |
152 ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels); | |
153 | |
154 PlusThreeBlockerCallback callback; | |
155 Blocker blocker(kChunkSize, | |
156 kBlockSize, | |
157 kNumInputChannels, | |
158 kNumOutputChannels, | |
159 kWindow, | |
160 kShiftAmount, | |
161 &callback); | |
162 | |
163 RunTest(&blocker, | |
164 kChunkSize, | |
165 kNumFrames, | |
166 input_cb.channels(), | |
167 input_chunk_cb.channels(), | |
168 actual_output_cb.channels(), | |
169 output_chunk_cb.channels(), | |
170 kNumInputChannels, | |
171 kNumOutputChannels); | |
172 | |
173 ValidateSignalEquality(expected_output_cb.channels(), | |
174 actual_output_cb.channels(), | |
175 kNumOutputChannels, | |
176 kNumFrames); | |
177 } | |
178 | |
179 TEST_F(BlockerTest, TestBlockerMutuallyPrimeShiftAndBlockSize) { | |
180 const size_t kNumInputChannels = 3; | |
181 const size_t kNumOutputChannels = 2; | |
182 const size_t kNumFrames = 12; | |
183 const size_t kBlockSize = 4; | |
184 const size_t kChunkSize = 6; | |
185 const size_t kShiftAmount = 3; | |
186 | |
187 const float kInput[kNumInputChannels][kNumFrames] = { | |
188 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, | |
189 {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, | |
190 {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; | |
191 ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels); | |
192 input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput)); | |
193 | |
194 const float kExpectedOutput[kNumOutputChannels][kNumFrames] = { | |
195 {6, 10, 10, 20, 10, 10, 20, 10, 10, 20, 10, 10}, | |
196 {6, 14, 14, 28, 14, 14, 28, 14, 14, 28, 14, 14}}; | |
197 ChannelBuffer<float> expected_output_cb(kNumFrames, kNumOutputChannels); | |
198 expected_output_cb.SetDataForTesting( | |
199 kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput)); | |
200 | |
201 const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; | |
202 | |
203 ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels); | |
204 ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels); | |
205 ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels); | |
206 | |
207 PlusThreeBlockerCallback callback; | |
208 Blocker blocker(kChunkSize, | |
209 kBlockSize, | |
210 kNumInputChannels, | |
211 kNumOutputChannels, | |
212 kWindow, | |
213 kShiftAmount, | |
214 &callback); | |
215 | |
216 RunTest(&blocker, | |
217 kChunkSize, | |
218 kNumFrames, | |
219 input_cb.channels(), | |
220 input_chunk_cb.channels(), | |
221 actual_output_cb.channels(), | |
222 output_chunk_cb.channels(), | |
223 kNumInputChannels, | |
224 kNumOutputChannels); | |
225 | |
226 ValidateSignalEquality(expected_output_cb.channels(), | |
227 actual_output_cb.channels(), | |
228 kNumOutputChannels, | |
229 kNumFrames); | |
230 } | |
231 | |
232 TEST_F(BlockerTest, TestBlockerNoOverlap) { | |
233 const size_t kNumInputChannels = 3; | |
234 const size_t kNumOutputChannels = 2; | |
235 const size_t kNumFrames = 12; | |
236 const size_t kBlockSize = 4; | |
237 const size_t kChunkSize = 4; | |
238 const size_t kShiftAmount = 4; | |
239 | |
240 const float kInput[kNumInputChannels][kNumFrames] = { | |
241 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, | |
242 {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, | |
243 {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}}; | |
244 ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels); | |
245 input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput)); | |
246 | |
247 const float kExpectedOutput[kNumOutputChannels][kNumFrames] = { | |
248 {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, | |
249 {14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}}; | |
250 ChannelBuffer<float> expected_output_cb(kNumFrames, kNumOutputChannels); | |
251 expected_output_cb.SetDataForTesting( | |
252 kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput)); | |
253 | |
254 const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f}; | |
255 | |
256 ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels); | |
257 ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels); | |
258 ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels); | |
259 | |
260 PlusThreeBlockerCallback callback; | |
261 Blocker blocker(kChunkSize, | |
262 kBlockSize, | |
263 kNumInputChannels, | |
264 kNumOutputChannels, | |
265 kWindow, | |
266 kShiftAmount, | |
267 &callback); | |
268 | |
269 RunTest(&blocker, | |
270 kChunkSize, | |
271 kNumFrames, | |
272 input_cb.channels(), | |
273 input_chunk_cb.channels(), | |
274 actual_output_cb.channels(), | |
275 output_chunk_cb.channels(), | |
276 kNumInputChannels, | |
277 kNumOutputChannels); | |
278 | |
279 ValidateSignalEquality(expected_output_cb.channels(), | |
280 actual_output_cb.channels(), | |
281 kNumOutputChannels, | |
282 kNumFrames); | |
283 } | |
284 | |
285 TEST_F(BlockerTest, InitialDelaysAreMinimum) { | |
286 const size_t kNumInputChannels = 3; | |
287 const size_t kNumOutputChannels = 2; | |
288 const size_t kNumFrames = 1280; | |
289 const size_t kChunkSize[] = | |
290 {80, 80, 80, 80, 80, 80, 160, 160, 160, 160, 160, 160}; | |
291 const size_t kBlockSize[] = | |
292 {64, 64, 64, 128, 128, 128, 128, 128, 128, 256, 256, 256}; | |
293 const size_t kShiftAmount[] = | |
294 {16, 32, 64, 32, 64, 128, 32, 64, 128, 64, 128, 256}; | |
295 const size_t kInitialDelay[] = | |
296 {48, 48, 48, 112, 112, 112, 96, 96, 96, 224, 224, 224}; | |
297 | |
298 float input[kNumInputChannels][kNumFrames]; | |
299 for (size_t i = 0; i < kNumInputChannels; ++i) { | |
300 for (size_t j = 0; j < kNumFrames; ++j) { | |
301 input[i][j] = i + 1; | |
302 } | |
303 } | |
304 ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels); | |
305 input_cb.SetDataForTesting(input[0], sizeof(input) / sizeof(**input)); | |
306 | |
307 ChannelBuffer<float> output_cb(kNumFrames, kNumOutputChannels); | |
308 | |
309 CopyBlockerCallback callback; | |
310 | |
311 for (size_t i = 0; i < arraysize(kChunkSize); ++i) { | |
312 std::unique_ptr<float[]> window(new float[kBlockSize[i]]); | |
313 for (size_t j = 0; j < kBlockSize[i]; ++j) { | |
314 window[j] = 1.f; | |
315 } | |
316 | |
317 ChannelBuffer<float> input_chunk_cb(kChunkSize[i], kNumInputChannels); | |
318 ChannelBuffer<float> output_chunk_cb(kChunkSize[i], kNumOutputChannels); | |
319 | |
320 Blocker blocker(kChunkSize[i], | |
321 kBlockSize[i], | |
322 kNumInputChannels, | |
323 kNumOutputChannels, | |
324 window.get(), | |
325 kShiftAmount[i], | |
326 &callback); | |
327 | |
328 RunTest(&blocker, | |
329 kChunkSize[i], | |
330 kNumFrames, | |
331 input_cb.channels(), | |
332 input_chunk_cb.channels(), | |
333 output_cb.channels(), | |
334 output_chunk_cb.channels(), | |
335 kNumInputChannels, | |
336 kNumOutputChannels); | |
337 | |
338 ValidateInitialDelay(output_cb.channels(), | |
339 kNumOutputChannels, | |
340 kNumFrames, | |
341 kInitialDelay[i]); | |
342 } | |
343 } | |
344 | |
345 } // namespace webrtc | |
OLD | NEW |