Home | History | Annotate | Download | only in common_audio
      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