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: 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: 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: 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 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 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 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 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 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 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 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