• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "modules/audio_coding/codecs/opus/test/blocker.h"
12 
13 #include <memory>
14 
15 #include "rtc_base/arraysize.h"
16 #include "test/gtest.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:
ProcessBlock(const float * const * input,size_t num_frames,size_t num_input_channels,size_t num_output_channels,float * const * output)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:
ProcessBlock(const float * const * input,size_t num_frames,size_t num_input_channels,size_t num_output_channels,float * const * output)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:
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)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, chunk_size, num_input_channels,
75                             num_output_channels, output_chunk);
76       CopyTo(output, start, 0, num_output_channels, chunk_size, output_chunk);
77 
78       start += chunk_size;
79       end += chunk_size;
80     }
81   }
82 
ValidateSignalEquality(const float * const * expected,const float * const * actual,size_t num_channels,size_t num_frames)83   void ValidateSignalEquality(const float* const* expected,
84                               const float* const* actual,
85                               size_t num_channels,
86                               size_t num_frames) {
87     for (size_t i = 0; i < num_channels; ++i) {
88       for (size_t j = 0; j < num_frames; ++j) {
89         EXPECT_FLOAT_EQ(expected[i][j], actual[i][j]);
90       }
91     }
92   }
93 
ValidateInitialDelay(const float * const * output,size_t num_channels,size_t num_frames,size_t initial_delay)94   void ValidateInitialDelay(const float* const* output,
95                             size_t num_channels,
96                             size_t num_frames,
97                             size_t initial_delay) {
98     for (size_t i = 0; i < num_channels; ++i) {
99       for (size_t 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 
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)109   static void CopyTo(float* const* dst,
110                      size_t start_index_dst,
111                      size_t start_index_src,
112                      size_t num_channels,
113                      size_t num_frames,
114                      const float* const* src) {
115     for (size_t i = 0; i < num_channels; ++i) {
116       memcpy(&dst[i][start_index_dst], &src[i][start_index_src],
117              num_frames * sizeof(float));
118     }
119   }
120 };
121 
TEST_F(BlockerTest,TestBlockerMutuallyPrimeChunkandBlockSize)122 TEST_F(BlockerTest, TestBlockerMutuallyPrimeChunkandBlockSize) {
123   const size_t kNumInputChannels = 3;
124   const size_t kNumOutputChannels = 2;
125   const size_t kNumFrames = 10;
126   const size_t kBlockSize = 4;
127   const size_t kChunkSize = 5;
128   const size_t kShiftAmount = 2;
129 
130   const float kInput[kNumInputChannels][kNumFrames] = {
131       {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
132       {2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
133       {3, 3, 3, 3, 3, 3, 3, 3, 3, 3}};
134   ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels);
135   input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput));
136 
137   const float kExpectedOutput[kNumInputChannels][kNumFrames] = {
138       {6, 6, 12, 20, 20, 20, 20, 20, 20, 20},
139       {6, 6, 12, 28, 28, 28, 28, 28, 28, 28}};
140   ChannelBuffer<float> expected_output_cb(kNumFrames, kNumInputChannels);
141   expected_output_cb.SetDataForTesting(
142       kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput));
143 
144   const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
145 
146   ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels);
147   ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels);
148   ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels);
149 
150   PlusThreeBlockerCallback callback;
151   Blocker blocker(kChunkSize, kBlockSize, kNumInputChannels, kNumOutputChannels,
152                   kWindow, kShiftAmount, &callback);
153 
154   RunTest(&blocker, kChunkSize, kNumFrames, input_cb.channels(),
155           input_chunk_cb.channels(), actual_output_cb.channels(),
156           output_chunk_cb.channels(), kNumInputChannels, kNumOutputChannels);
157 
158   ValidateSignalEquality(expected_output_cb.channels(),
159                          actual_output_cb.channels(), kNumOutputChannels,
160                          kNumFrames);
161 }
162 
TEST_F(BlockerTest,TestBlockerMutuallyPrimeShiftAndBlockSize)163 TEST_F(BlockerTest, TestBlockerMutuallyPrimeShiftAndBlockSize) {
164   const size_t kNumInputChannels = 3;
165   const size_t kNumOutputChannels = 2;
166   const size_t kNumFrames = 12;
167   const size_t kBlockSize = 4;
168   const size_t kChunkSize = 6;
169   const size_t kShiftAmount = 3;
170 
171   const float kInput[kNumInputChannels][kNumFrames] = {
172       {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
173       {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
174       {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}};
175   ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels);
176   input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput));
177 
178   const float kExpectedOutput[kNumOutputChannels][kNumFrames] = {
179       {6, 10, 10, 20, 10, 10, 20, 10, 10, 20, 10, 10},
180       {6, 14, 14, 28, 14, 14, 28, 14, 14, 28, 14, 14}};
181   ChannelBuffer<float> expected_output_cb(kNumFrames, kNumOutputChannels);
182   expected_output_cb.SetDataForTesting(
183       kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput));
184 
185   const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
186 
187   ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels);
188   ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels);
189   ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels);
190 
191   PlusThreeBlockerCallback callback;
192   Blocker blocker(kChunkSize, kBlockSize, kNumInputChannels, kNumOutputChannels,
193                   kWindow, kShiftAmount, &callback);
194 
195   RunTest(&blocker, kChunkSize, kNumFrames, input_cb.channels(),
196           input_chunk_cb.channels(), actual_output_cb.channels(),
197           output_chunk_cb.channels(), kNumInputChannels, kNumOutputChannels);
198 
199   ValidateSignalEquality(expected_output_cb.channels(),
200                          actual_output_cb.channels(), kNumOutputChannels,
201                          kNumFrames);
202 }
203 
TEST_F(BlockerTest,TestBlockerNoOverlap)204 TEST_F(BlockerTest, TestBlockerNoOverlap) {
205   const size_t kNumInputChannels = 3;
206   const size_t kNumOutputChannels = 2;
207   const size_t kNumFrames = 12;
208   const size_t kBlockSize = 4;
209   const size_t kChunkSize = 4;
210   const size_t kShiftAmount = 4;
211 
212   const float kInput[kNumInputChannels][kNumFrames] = {
213       {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
214       {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
215       {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}};
216   ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels);
217   input_cb.SetDataForTesting(kInput[0], sizeof(kInput) / sizeof(**kInput));
218 
219   const float kExpectedOutput[kNumOutputChannels][kNumFrames] = {
220       {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
221       {14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}};
222   ChannelBuffer<float> expected_output_cb(kNumFrames, kNumOutputChannels);
223   expected_output_cb.SetDataForTesting(
224       kExpectedOutput[0], sizeof(kExpectedOutput) / sizeof(**kExpectedOutput));
225 
226   const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
227 
228   ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels);
229   ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels);
230   ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels);
231 
232   PlusThreeBlockerCallback callback;
233   Blocker blocker(kChunkSize, kBlockSize, kNumInputChannels, kNumOutputChannels,
234                   kWindow, kShiftAmount, &callback);
235 
236   RunTest(&blocker, kChunkSize, kNumFrames, input_cb.channels(),
237           input_chunk_cb.channels(), actual_output_cb.channels(),
238           output_chunk_cb.channels(), kNumInputChannels, kNumOutputChannels);
239 
240   ValidateSignalEquality(expected_output_cb.channels(),
241                          actual_output_cb.channels(), kNumOutputChannels,
242                          kNumFrames);
243 }
244 
TEST_F(BlockerTest,InitialDelaysAreMinimum)245 TEST_F(BlockerTest, InitialDelaysAreMinimum) {
246   const size_t kNumInputChannels = 3;
247   const size_t kNumOutputChannels = 2;
248   const size_t kNumFrames = 1280;
249   const size_t kChunkSize[] = {80,  80,  80,  80,  80,  80,
250                                160, 160, 160, 160, 160, 160};
251   const size_t kBlockSize[] = {64,  64,  64,  128, 128, 128,
252                                128, 128, 128, 256, 256, 256};
253   const size_t kShiftAmount[] = {16, 32, 64,  32, 64,  128,
254                                  32, 64, 128, 64, 128, 256};
255   const size_t kInitialDelay[] = {48, 48, 48, 112, 112, 112,
256                                   96, 96, 96, 224, 224, 224};
257 
258   float input[kNumInputChannels][kNumFrames];
259   for (size_t i = 0; i < kNumInputChannels; ++i) {
260     for (size_t j = 0; j < kNumFrames; ++j) {
261       input[i][j] = i + 1;
262     }
263   }
264   ChannelBuffer<float> input_cb(kNumFrames, kNumInputChannels);
265   input_cb.SetDataForTesting(input[0], sizeof(input) / sizeof(**input));
266 
267   ChannelBuffer<float> output_cb(kNumFrames, kNumOutputChannels);
268 
269   CopyBlockerCallback callback;
270 
271   for (size_t i = 0; i < arraysize(kChunkSize); ++i) {
272     std::unique_ptr<float[]> window(new float[kBlockSize[i]]);
273     for (size_t j = 0; j < kBlockSize[i]; ++j) {
274       window[j] = 1.f;
275     }
276 
277     ChannelBuffer<float> input_chunk_cb(kChunkSize[i], kNumInputChannels);
278     ChannelBuffer<float> output_chunk_cb(kChunkSize[i], kNumOutputChannels);
279 
280     Blocker blocker(kChunkSize[i], kBlockSize[i], kNumInputChannels,
281                     kNumOutputChannels, window.get(), kShiftAmount[i],
282                     &callback);
283 
284     RunTest(&blocker, kChunkSize[i], kNumFrames, input_cb.channels(),
285             input_chunk_cb.channels(), output_cb.channels(),
286             output_chunk_cb.channels(), kNumInputChannels, kNumOutputChannels);
287 
288     ValidateInitialDelay(output_cb.channels(), kNumOutputChannels, kNumFrames,
289                          kInitialDelay[i]);
290   }
291 }
292 
293 }  // namespace webrtc
294