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