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