OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/modules/audio_mixer/frame_combiner.h" | 11 #include "webrtc/modules/audio_mixer/frame_combiner.h" |
12 | 12 |
13 #include <numeric> | 13 #include <numeric> |
14 #include <sstream> | 14 #include <sstream> |
15 #include <string> | 15 #include <string> |
16 | 16 |
17 #include "webrtc/audio/utility/audio_frame_operations.h" | |
17 #include "webrtc/base/checks.h" | 18 #include "webrtc/base/checks.h" |
19 #include "webrtc/modules/audio_mixer/gain_change_calculator.h" | |
20 #include "webrtc/modules/audio_mixer/sine_wave_generator.h" | |
18 #include "webrtc/test/gtest.h" | 21 #include "webrtc/test/gtest.h" |
19 | 22 |
20 namespace webrtc { | 23 namespace webrtc { |
21 | 24 |
22 namespace { | 25 namespace { |
23 std::string ProduceDebugText(int sample_rate_hz, | 26 std::string ProduceDebugText(int sample_rate_hz, |
24 int number_of_channels, | 27 int number_of_channels, |
25 int number_of_sources) { | 28 int number_of_sources) { |
26 std::ostringstream ss; | 29 std::ostringstream ss; |
27 ss << "Sample rate: " << sample_rate_hz << " "; | 30 ss << "Sample rate: " << sample_rate_hz << " ,"; |
28 ss << "Number of channels: " << number_of_channels << " "; | 31 ss << "number of channels: " << number_of_channels << " ,"; |
29 ss << "Number of sources: " << number_of_sources; | 32 ss << "number of sources: " << number_of_sources; |
30 return ss.str(); | 33 return ss.str(); |
31 } | 34 } |
32 | 35 |
36 std::string ProduceDebugText(int sample_rate_hz, | |
37 int number_of_channels, | |
38 int number_of_sources, | |
39 bool limiter_active, | |
40 float wave_frequency) { | |
41 std::ostringstream ss; | |
42 ss << "Sample rate: " << sample_rate_hz << " ,"; | |
43 ss << "number of channels: " << number_of_channels << " ,"; | |
44 ss << "number of sources: " << number_of_sources << " ,"; | |
45 ss << "limiter active: " << limiter_active << " ,"; | |
hlundin-webrtc
2017/03/29 07:22:55
Does the bool translate to something human-readabl
aleloi
2017/03/29 10:56:11
Now it does! (thx)
| |
46 ss << "wave frequency: " << wave_frequency << " ,"; | |
47 return ss.str(); | |
48 } | |
49 | |
33 AudioFrame frame1; | 50 AudioFrame frame1; |
34 AudioFrame frame2; | 51 AudioFrame frame2; |
35 AudioFrame audio_frame_for_mixing; | 52 AudioFrame audio_frame_for_mixing; |
36 | 53 |
37 void SetUpFrames(int sample_rate_hz, int number_of_channels) { | 54 void SetUpFrames(int sample_rate_hz, int number_of_channels) { |
38 for (auto* frame : {&frame1, &frame2}) { | 55 for (auto* frame : {&frame1, &frame2}) { |
39 frame->UpdateFrame(-1, 0, nullptr, | 56 frame->UpdateFrame(-1, 0, nullptr, |
40 rtc::CheckedDivExact(sample_rate_hz, 100), | 57 rtc::CheckedDivExact(sample_rate_hz, 100), |
41 sample_rate_hz, AudioFrame::kNormalSpeech, | 58 sample_rate_hz, AudioFrame::kNormalSpeech, |
42 AudioFrame::kVadActive, number_of_channels); | 59 AudioFrame::kVadActive, number_of_channels); |
43 } | 60 } |
44 } | 61 } |
45 } // namespace | 62 } // namespace |
46 | 63 |
47 TEST(FrameCombiner, BasicApiCallsLimiter) { | 64 TEST(FrameCombiner, BasicApiCallsLimiter) { |
48 FrameCombiner combiner(true); | 65 FrameCombiner combiner(true); |
49 for (const int rate : {8000, 16000, 32000, 48000}) { | 66 for (const int rate : {8000, 16000, 32000, 48000}) { |
50 for (const int number_of_channels : {1, 2}) { | 67 for (const int number_of_channels : {1, 2}) { |
51 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2}; | 68 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2}; |
52 SetUpFrames(rate, number_of_channels); | 69 SetUpFrames(rate, number_of_channels); |
53 | 70 |
54 for (const int number_of_frames : {0, 1, 2}) { | 71 for (const int number_of_frames : {0, 1, 2}) { |
55 SCOPED_TRACE( | 72 SCOPED_TRACE( |
56 ProduceDebugText(rate, number_of_channels, number_of_frames)); | 73 ProduceDebugText(rate, number_of_channels, number_of_frames)); |
57 const std::vector<AudioFrame*> frames_to_combine( | 74 const std::vector<AudioFrame*> frames_to_combine( |
58 all_frames.begin(), all_frames.begin() + number_of_frames); | 75 all_frames.begin(), all_frames.begin() + number_of_frames); |
59 combiner.Combine(frames_to_combine, number_of_channels, rate, | 76 combiner.Combine(frames_to_combine, number_of_channels, rate, |
60 &audio_frame_for_mixing); | 77 frames_to_combine.size(), &audio_frame_for_mixing); |
61 } | 78 } |
62 } | 79 } |
63 } | 80 } |
64 } | 81 } |
65 | 82 |
66 // No APM limiter means no AudioProcessing::NativeRate restriction | 83 // No APM limiter means no AudioProcessing::NativeRate restriction |
67 // on rate. The rate has to be divisible by 100 since we use | 84 // on rate. The rate has to be divisible by 100 since we use |
68 // 10 ms frames, though. | 85 // 10 ms frames, though. |
69 TEST(FrameCombiner, BasicApiCallsNoLimiter) { | 86 TEST(FrameCombiner, BasicApiCallsNoLimiter) { |
70 FrameCombiner combiner(false); | 87 FrameCombiner combiner(false); |
71 for (const int rate : {8000, 10000, 11000, 32000, 44100}) { | 88 for (const int rate : {8000, 10000, 11000, 32000, 44100}) { |
72 for (const int number_of_channels : {1, 2}) { | 89 for (const int number_of_channels : {1, 2}) { |
73 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2}; | 90 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2}; |
74 SetUpFrames(rate, number_of_channels); | 91 SetUpFrames(rate, number_of_channels); |
75 | 92 |
76 for (const int number_of_frames : {0, 1, 2}) { | 93 for (const int number_of_frames : {0, 1, 2}) { |
77 SCOPED_TRACE( | 94 SCOPED_TRACE( |
78 ProduceDebugText(rate, number_of_channels, number_of_frames)); | 95 ProduceDebugText(rate, number_of_channels, number_of_frames)); |
79 const std::vector<AudioFrame*> frames_to_combine( | 96 const std::vector<AudioFrame*> frames_to_combine( |
80 all_frames.begin(), all_frames.begin() + number_of_frames); | 97 all_frames.begin(), all_frames.begin() + number_of_frames); |
81 combiner.Combine(frames_to_combine, number_of_channels, rate, | 98 combiner.Combine(frames_to_combine, number_of_channels, rate, |
82 &audio_frame_for_mixing); | 99 frames_to_combine.size(), &audio_frame_for_mixing); |
83 } | 100 } |
84 } | 101 } |
85 } | 102 } |
86 } | 103 } |
87 | 104 |
88 TEST(FrameCombiner, CombiningZeroFramesShouldProduceSilence) { | 105 TEST(FrameCombiner, CombiningZeroFramesShouldProduceSilence) { |
89 FrameCombiner combiner(false); | 106 FrameCombiner combiner(false); |
90 for (const int rate : {8000, 10000, 11000, 32000, 44100}) { | 107 for (const int rate : {8000, 10000, 11000, 32000, 44100}) { |
91 for (const int number_of_channels : {1, 2}) { | 108 for (const int number_of_channels : {1, 2}) { |
92 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 0)); | 109 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 0)); |
93 | 110 |
94 const std::vector<AudioFrame*> frames_to_combine; | 111 const std::vector<AudioFrame*> frames_to_combine; |
95 combiner.Combine(frames_to_combine, number_of_channels, rate, | 112 combiner.Combine(frames_to_combine, number_of_channels, rate, |
96 &audio_frame_for_mixing); | 113 frames_to_combine.size(), &audio_frame_for_mixing); |
97 | 114 |
98 const std::vector<int16_t> mixed_data( | 115 const std::vector<int16_t> mixed_data( |
99 audio_frame_for_mixing.data_, | 116 audio_frame_for_mixing.data_, |
100 audio_frame_for_mixing.data_ + number_of_channels * rate / 100); | 117 audio_frame_for_mixing.data_ + number_of_channels * rate / 100); |
101 | 118 |
102 const std::vector<int16_t> expected(number_of_channels * rate / 100, 0); | 119 const std::vector<int16_t> expected(number_of_channels * rate / 100, 0); |
103 EXPECT_EQ(mixed_data, expected); | 120 EXPECT_EQ(mixed_data, expected); |
104 } | 121 } |
105 } | 122 } |
106 } | 123 } |
107 | 124 |
108 TEST(FrameCombiner, CombiningOneFrameShouldNotChangeFrame) { | 125 TEST(FrameCombiner, CombiningOneFrameShouldNotChangeFrame) { |
109 FrameCombiner combiner(false); | 126 FrameCombiner combiner(false); |
110 for (const int rate : {8000, 10000, 11000, 32000, 44100}) { | 127 for (const int rate : {8000, 10000, 11000, 32000, 44100}) { |
111 for (const int number_of_channels : {1, 2}) { | 128 for (const int number_of_channels : {1, 2}) { |
112 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1)); | 129 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1)); |
113 | 130 |
114 SetUpFrames(rate, number_of_channels); | 131 SetUpFrames(rate, number_of_channels); |
115 std::iota(frame1.data_, frame1.data_ + number_of_channels * rate / 100, | 132 std::iota(frame1.data_, frame1.data_ + number_of_channels * rate / 100, |
116 0); | 133 0); |
117 const std::vector<AudioFrame*> frames_to_combine = {&frame1}; | 134 const std::vector<AudioFrame*> frames_to_combine = {&frame1}; |
118 combiner.Combine(frames_to_combine, number_of_channels, rate, | 135 combiner.Combine(frames_to_combine, number_of_channels, rate, |
119 &audio_frame_for_mixing); | 136 frames_to_combine.size(), &audio_frame_for_mixing); |
120 | 137 |
121 const std::vector<int16_t> mixed_data( | 138 const std::vector<int16_t> mixed_data( |
122 audio_frame_for_mixing.data_, | 139 audio_frame_for_mixing.data_, |
123 audio_frame_for_mixing.data_ + number_of_channels * rate / 100); | 140 audio_frame_for_mixing.data_ + number_of_channels * rate / 100); |
124 | 141 |
125 std::vector<int16_t> expected(number_of_channels * rate / 100); | 142 std::vector<int16_t> expected(number_of_channels * rate / 100); |
126 std::iota(expected.begin(), expected.end(), 0); | 143 std::iota(expected.begin(), expected.end(), 0); |
127 EXPECT_EQ(mixed_data, expected); | 144 EXPECT_EQ(mixed_data, expected); |
128 } | 145 } |
129 } | 146 } |
130 } | 147 } |
131 | 148 |
149 // Send a sine curve through the FrameCombiner, and check that the | |
hlundin-webrtc
2017/03/29 07:22:55
curve -> wave
imho
aleloi
2017/03/29 10:56:11
Done.
| |
150 // difference between input and output varies smoothly. This is to | |
151 // catch issues like chromium:695993. | |
152 TEST(FrameCombiner, GainCurveIsSmoothForAlternatingNumberOfStreams) { | |
153 // Test doesn't work with rates requiring a band split, because it | |
154 // introduces a small delay measured in single samples, and this | |
155 // test cannot handle it. | |
156 // | |
157 // TODO(aleloi): Add more rates when APM limiter doesn't use band | |
158 // split. | |
159 for (const bool use_limiter : {true, false}) { | |
160 for (const int rate : {8000, 16000}) { | |
161 const int number_of_channels = 2; | |
hlundin-webrtc
2017/03/29 07:22:55
constexpr
(I guess you are preparing to iterate ov
aleloi
2017/03/29 10:56:11
Done.
| |
162 for (const float wave_frequency : {50, 400, 3200}) { | |
163 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1, use_limiter, | |
164 wave_frequency)); | |
165 | |
166 FrameCombiner combiner(use_limiter); | |
167 | |
168 constexpr int16_t wave_amplitude = 30000; | |
169 SineWaveGenerator wave_generator(wave_frequency, wave_amplitude); | |
170 | |
171 GainChangeCalculator change_calculator; | |
172 float cumulative_change = 0.f; | |
173 | |
174 constexpr int16_t iterations = 100; | |
175 | |
176 for (int i = 0; i < iterations; ++i) { | |
hlundin-webrtc
2017/03/29 07:22:55
This is a bit of a mixture. int i and int16_t iter
aleloi
2017/03/29 10:56:11
Oh, there is no reason for iterations to be int16_
| |
177 SetUpFrames(rate, number_of_channels); | |
178 wave_generator.GenerateNextFrame(&frame1); | |
179 AudioFrameOperations::Mute(&frame2); | |
180 | |
181 std::vector<AudioFrame*> frames_to_combine = {&frame1}; | |
182 if (i % 2 == 0) { | |
183 frames_to_combine.push_back(&frame2); | |
184 } | |
185 const size_t number_of_samples = | |
186 frame1.samples_per_channel_ * number_of_channels; | |
187 | |
188 // Ensures limiter is on if 'use_limiter'. | |
189 const size_t number_of_streams = 2; | |
hlundin-webrtc
2017/03/29 07:22:55
constexpr
aleloi
2017/03/29 10:56:11
Done.
| |
190 combiner.Combine(frames_to_combine, number_of_channels, rate, | |
191 number_of_streams, &audio_frame_for_mixing); | |
192 cumulative_change += change_calculator.CalculateGainChange( | |
193 rtc::ArrayView<const int16_t>(frame1.data_, number_of_samples), | |
194 rtc::ArrayView<const int16_t>(audio_frame_for_mixing.data_, | |
195 number_of_samples)); | |
196 } | |
197 RTC_DCHECK_LT(cumulative_change, 10); | |
198 } | |
199 } | |
200 } | |
201 } | |
132 } // namespace webrtc | 202 } // namespace webrtc |
OLD | NEW |