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