Chromium Code Reviews| 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 |