1 /*
2  *  Copyright (c) 2017 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_processing/aec3/decimator.h"
12 
13 #include <math.h>
14 #include <algorithm>
15 #include <array>
16 #include <numeric>
17 #include <sstream>
18 #include <string>
19 #include <vector>
20 
21 #include "modules/audio_processing/aec3/aec3_common.h"
22 #include "test/gtest.h"
23 
24 namespace webrtc {
25 
26 namespace {
27 
ProduceDebugText(int sample_rate_hz)28 std::string ProduceDebugText(int sample_rate_hz) {
29   std::ostringstream ss;
30   ss << "Sample rate: " << sample_rate_hz;
31   return ss.str();
32 }
33 
34 constexpr size_t kDownSamplingFactors[] = {2, 4, 8};
35 constexpr float kPi = 3.141592f;
36 constexpr size_t kNumStartupBlocks = 50;
37 constexpr size_t kNumBlocks = 1000;
38 
ProduceDecimatedSinusoidalOutputPower(int sample_rate_hz,size_t down_sampling_factor,float sinusoidal_frequency_hz,float * input_power,float * output_power)39 void ProduceDecimatedSinusoidalOutputPower(int sample_rate_hz,
40                                            size_t down_sampling_factor,
41                                            float sinusoidal_frequency_hz,
42                                            float* input_power,
43                                            float* output_power) {
44   float input[kBlockSize * kNumBlocks];
45   const size_t sub_block_size = kBlockSize / down_sampling_factor;
46 
47   // Produce a sinusoid of the specified frequency.
48   for (size_t k = 0; k < kBlockSize * kNumBlocks; ++k) {
49     input[k] =
50         32767.f * sin(2.f * kPi * sinusoidal_frequency_hz * k / sample_rate_hz);
51   }
52 
53   Decimator decimator(down_sampling_factor);
54   std::vector<float> output(sub_block_size * kNumBlocks);
55 
56   for (size_t k = 0; k < kNumBlocks; ++k) {
57     std::vector<float> sub_block(sub_block_size);
58 
59     decimator.Decimate(
60         rtc::ArrayView<const float>(&input[k * kBlockSize], kBlockSize),
61         sub_block);
62 
63     std::copy(sub_block.begin(), sub_block.end(),
64               output.begin() + k * sub_block_size);
65   }
66 
67   ASSERT_GT(kNumBlocks, kNumStartupBlocks);
68   rtc::ArrayView<const float> input_to_evaluate(
69       &input[kNumStartupBlocks * kBlockSize],
70       (kNumBlocks - kNumStartupBlocks) * kBlockSize);
71   rtc::ArrayView<const float> output_to_evaluate(
72       &output[kNumStartupBlocks * sub_block_size],
73       (kNumBlocks - kNumStartupBlocks) * sub_block_size);
74   *input_power =
75       std::inner_product(input_to_evaluate.begin(), input_to_evaluate.end(),
76                          input_to_evaluate.begin(), 0.f) /
77       input_to_evaluate.size();
78   *output_power =
79       std::inner_product(output_to_evaluate.begin(), output_to_evaluate.end(),
80                          output_to_evaluate.begin(), 0.f) /
81       output_to_evaluate.size();
82 }
83 
84 }  // namespace
85 
86 // Verifies that there is little aliasing from upper frequencies in the
87 // downsampling.
TEST(Decimator,NoLeakageFromUpperFrequencies)88 TEST(Decimator, NoLeakageFromUpperFrequencies) {
89   float input_power;
90   float output_power;
91   for (auto rate : {8000, 16000, 32000, 48000}) {
92     for (auto down_sampling_factor : kDownSamplingFactors) {
93       ProduceDebugText(rate);
94       ProduceDecimatedSinusoidalOutputPower(rate, down_sampling_factor,
95                                             3.f / 8.f * rate, &input_power,
96                                             &output_power);
97       EXPECT_GT(0.0001f * input_power, output_power);
98     }
99   }
100 }
101 
102 // Verifies that the impact of low-frequency content is small during the
103 // downsampling.
TEST(Decimator,NoImpactOnLowerFrequencies)104 TEST(Decimator, NoImpactOnLowerFrequencies) {
105   float input_power;
106   float output_power;
107   for (auto rate : {8000, 16000, 32000, 48000}) {
108     for (auto down_sampling_factor : kDownSamplingFactors) {
109       ProduceDebugText(rate);
110       ProduceDecimatedSinusoidalOutputPower(rate, down_sampling_factor, 200.f,
111                                             &input_power, &output_power);
112       EXPECT_LT(0.7f * input_power, output_power);
113     }
114   }
115 }
116 
117 #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
118 // Verifies the check for the input size.
TEST(Decimator,WrongInputSize)119 TEST(Decimator, WrongInputSize) {
120   Decimator decimator(4);
121   std::vector<float> x(std::vector<float>(kBlockSize - 1, 0.f));
122   std::array<float, kBlockSize / 4> x_downsampled;
123   EXPECT_DEATH(decimator.Decimate(x, x_downsampled), "");
124 }
125 
126 // Verifies the check for non-null output parameter.
TEST(Decimator,NullOutput)127 TEST(Decimator, NullOutput) {
128   Decimator decimator(4);
129   std::vector<float> x(std::vector<float>(kBlockSize, 0.f));
130   EXPECT_DEATH(decimator.Decimate(x, nullptr), "");
131 }
132 
133 // Verifies the check for the output size.
TEST(Decimator,WrongOutputSize)134 TEST(Decimator, WrongOutputSize) {
135   Decimator decimator(4);
136   std::vector<float> x(std::vector<float>(kBlockSize, 0.f));
137   std::array<float, kBlockSize / 4 - 1> x_downsampled;
138   EXPECT_DEATH(decimator.Decimate(x, x_downsampled), "");
139 }
140 
141 // Verifies the check for the correct downsampling factor.
TEST(Decimator,CorrectDownSamplingFactor)142 TEST(Decimator, CorrectDownSamplingFactor) {
143   EXPECT_DEATH(Decimator(3), "");
144 }
145 
146 #endif
147 
148 }  // namespace webrtc
149