blob: 5f024a4a55659a5752809c15f4cfb7622258814e [file] [log] [blame]
aleloi24899e52017-02-21 13:06:291/*
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
Mirko Bonadei92ea95e2017-09-15 04:47:3111#include "modules/audio_mixer/frame_combiner.h"
aleloi24899e52017-02-21 13:06:2912
Yves Gerey3e707812018-11-28 15:47:4913#include <cstdint>
14#include <initializer_list>
aleloi24899e52017-02-21 13:06:2915#include <numeric>
aleloi24899e52017-02-21 13:06:2916#include <string>
Yves Gerey3e707812018-11-28 15:47:4917#include <type_traits>
aleloi24899e52017-02-21 13:06:2918
Yves Gerey3e707812018-11-28 15:47:4919#include "api/array_view.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3120#include "audio/utility/audio_frame_operations.h"
21#include "modules/audio_mixer/gain_change_calculator.h"
22#include "modules/audio_mixer/sine_wave_generator.h"
23#include "rtc_base/checks.h"
Jonas Olsson366a50c2018-09-06 11:41:3024#include "rtc_base/strings/string_builder.h"
Alex Loikob4977de2019-01-28 15:38:3825#include "test/gmock.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3126#include "test/gtest.h"
aleloi24899e52017-02-21 13:06:2927
28namespace webrtc {
29
30namespace {
Alex Loiko507e8d12018-02-27 12:51:4731using LimiterType = FrameCombiner::LimiterType;
Alex Loiko507e8d12018-02-27 12:51:4732struct FrameCombinerConfig {
Alex Loiko8396e342018-06-21 10:04:0533 bool use_limiter;
34 int sample_rate_hz;
Alex Loiko507e8d12018-02-27 12:51:4735 int number_of_channels;
36 float wave_frequency;
37};
38
aleloi24899e52017-02-21 13:06:2939std::string ProduceDebugText(int sample_rate_hz,
40 int number_of_channels,
41 int number_of_sources) {
Jonas Olsson366a50c2018-09-06 11:41:3042 rtc::StringBuilder ss;
aleloi2c9306e2017-03-29 11:25:1643 ss << "Sample rate: " << sample_rate_hz << " ,";
44 ss << "number of channels: " << number_of_channels << " ,";
45 ss << "number of sources: " << number_of_sources;
Jonas Olsson84df1c72018-09-14 14:59:3246 return ss.Release();
aleloi2c9306e2017-03-29 11:25:1647}
48
Alex Loiko507e8d12018-02-27 12:51:4749std::string ProduceDebugText(const FrameCombinerConfig& config) {
Jonas Olsson366a50c2018-09-06 11:41:3050 rtc::StringBuilder ss;
Alex Loiko507e8d12018-02-27 12:51:4751 ss << "Sample rate: " << config.sample_rate_hz << " ,";
52 ss << "number of channels: " << config.number_of_channels << " ,";
Alex Loiko8396e342018-06-21 10:04:0553 ss << "limiter active: " << (config.use_limiter ? "on" : "off") << " ,";
Alex Loiko507e8d12018-02-27 12:51:4754 ss << "wave frequency: " << config.wave_frequency << " ,";
Jonas Olsson84df1c72018-09-14 14:59:3255 return ss.Release();
aleloi24899e52017-02-21 13:06:2956}
57
58AudioFrame frame1;
59AudioFrame frame2;
60AudioFrame audio_frame_for_mixing;
61
62void SetUpFrames(int sample_rate_hz, int number_of_channels) {
63 for (auto* frame : {&frame1, &frame2}) {
solenbergc7b4a452017-09-28 14:37:1164 frame->UpdateFrame(0, nullptr, rtc::CheckedDivExact(sample_rate_hz, 100),
aleloi24899e52017-02-21 13:06:2965 sample_rate_hz, AudioFrame::kNormalSpeech,
66 AudioFrame::kVadActive, number_of_channels);
67 }
68}
69} // namespace
70
Alex Loiko8396e342018-06-21 10:04:0571// The limiter requires sample rate divisible by 2000.
aleloi24899e52017-02-21 13:06:2972TEST(FrameCombiner, BasicApiCallsLimiter) {
Alex Loiko8396e342018-06-21 10:04:0573 FrameCombiner combiner(true);
74 for (const int rate : {8000, 18000, 34000, 48000}) {
Alex Loikob4977de2019-01-28 15:38:3875 for (const int number_of_channels : {1, 2, 4, 8}) {
aleloi24899e52017-02-21 13:06:2976 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
77 SetUpFrames(rate, number_of_channels);
78
79 for (const int number_of_frames : {0, 1, 2}) {
80 SCOPED_TRACE(
81 ProduceDebugText(rate, number_of_channels, number_of_frames));
82 const std::vector<AudioFrame*> frames_to_combine(
83 all_frames.begin(), all_frames.begin() + number_of_frames);
84 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 11:25:1685 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 13:06:2986 }
87 }
88 }
89}
90
Alex Loikob4977de2019-01-28 15:38:3891// There are DCHECKs in place to check for invalid parameters.
92TEST(FrameCombiner, DebugBuildCrashesWithManyChannels) {
93 FrameCombiner combiner(true);
94 for (const int rate : {8000, 18000, 34000, 48000}) {
95 for (const int number_of_channels : {10, 20, 21}) {
96 if (static_cast<size_t>(rate / 100 * number_of_channels) >
97 AudioFrame::kMaxDataSizeSamples) {
98 continue;
99 }
100 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
101 SetUpFrames(rate, number_of_channels);
102
103 const int number_of_frames = 2;
104 SCOPED_TRACE(
105 ProduceDebugText(rate, number_of_channels, number_of_frames));
106 const std::vector<AudioFrame*> frames_to_combine(
107 all_frames.begin(), all_frames.begin() + number_of_frames);
108#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
109 EXPECT_DEATH(
110 combiner.Combine(frames_to_combine, number_of_channels, rate,
111 frames_to_combine.size(), &audio_frame_for_mixing),
112 "");
113#elif !RTC_DCHECK_IS_ON
114 combiner.Combine(frames_to_combine, number_of_channels, rate,
115 frames_to_combine.size(), &audio_frame_for_mixing);
116#endif
117 }
118 }
119}
120
121TEST(FrameCombiner, DebugBuildCrashesWithHighRate) {
122 FrameCombiner combiner(true);
123 for (const int rate : {50000, 96000, 128000, 196000}) {
124 for (const int number_of_channels : {1, 2, 3}) {
125 if (static_cast<size_t>(rate / 100 * number_of_channels) >
126 AudioFrame::kMaxDataSizeSamples) {
127 continue;
128 }
129 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
130 SetUpFrames(rate, number_of_channels);
131
132 const int number_of_frames = 2;
133 SCOPED_TRACE(
134 ProduceDebugText(rate, number_of_channels, number_of_frames));
135 const std::vector<AudioFrame*> frames_to_combine(
136 all_frames.begin(), all_frames.begin() + number_of_frames);
137#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
138 EXPECT_DEATH(
139 combiner.Combine(frames_to_combine, number_of_channels, rate,
140 frames_to_combine.size(), &audio_frame_for_mixing),
141 "");
142#elif !RTC_DCHECK_IS_ON
143 combiner.Combine(frames_to_combine, number_of_channels, rate,
144 frames_to_combine.size(), &audio_frame_for_mixing);
145#endif
146 }
147 }
148}
149
Alex Loiko8396e342018-06-21 10:04:05150// With no limiter, the rate has to be divisible by 100 since we use
151// 10 ms frames.
aleloi24899e52017-02-21 13:06:29152TEST(FrameCombiner, BasicApiCallsNoLimiter) {
Alex Loiko8396e342018-06-21 10:04:05153 FrameCombiner combiner(false);
aleloi24899e52017-02-21 13:06:29154 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
Alex Loikob4977de2019-01-28 15:38:38155 for (const int number_of_channels : {1, 2, 4, 8}) {
aleloi24899e52017-02-21 13:06:29156 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
157 SetUpFrames(rate, number_of_channels);
158
159 for (const int number_of_frames : {0, 1, 2}) {
160 SCOPED_TRACE(
161 ProduceDebugText(rate, number_of_channels, number_of_frames));
162 const std::vector<AudioFrame*> frames_to_combine(
163 all_frames.begin(), all_frames.begin() + number_of_frames);
164 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 11:25:16165 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 13:06:29166 }
167 }
168 }
169}
170
171TEST(FrameCombiner, CombiningZeroFramesShouldProduceSilence) {
Alex Loiko8396e342018-06-21 10:04:05172 FrameCombiner combiner(false);
aleloi24899e52017-02-21 13:06:29173 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
174 for (const int number_of_channels : {1, 2}) {
175 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 0));
176
177 const std::vector<AudioFrame*> frames_to_combine;
178 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 11:25:16179 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 13:06:29180
yujo36b1a5f2017-06-12 19:45:32181 const int16_t* audio_frame_for_mixing_data =
182 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 13:06:29183 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 19:45:32184 audio_frame_for_mixing_data,
185 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 13:06:29186
187 const std::vector<int16_t> expected(number_of_channels * rate / 100, 0);
188 EXPECT_EQ(mixed_data, expected);
189 }
190 }
191}
192
193TEST(FrameCombiner, CombiningOneFrameShouldNotChangeFrame) {
Alex Loiko8396e342018-06-21 10:04:05194 FrameCombiner combiner(false);
aleloi24899e52017-02-21 13:06:29195 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
Alex Loikob4977de2019-01-28 15:38:38196 for (const int number_of_channels : {1, 2, 4, 8, 10}) {
aleloi24899e52017-02-21 13:06:29197 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1));
198
199 SetUpFrames(rate, number_of_channels);
yujo36b1a5f2017-06-12 19:45:32200 int16_t* frame1_data = frame1.mutable_data();
201 std::iota(frame1_data, frame1_data + number_of_channels * rate / 100, 0);
aleloi24899e52017-02-21 13:06:29202 const std::vector<AudioFrame*> frames_to_combine = {&frame1};
203 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 11:25:16204 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 13:06:29205
yujo36b1a5f2017-06-12 19:45:32206 const int16_t* audio_frame_for_mixing_data =
207 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 13:06:29208 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 19:45:32209 audio_frame_for_mixing_data,
210 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 13:06:29211
212 std::vector<int16_t> expected(number_of_channels * rate / 100);
213 std::iota(expected.begin(), expected.end(), 0);
214 EXPECT_EQ(mixed_data, expected);
215 }
216 }
217}
218
aleloi2c9306e2017-03-29 11:25:16219// Send a sine wave through the FrameCombiner, and check that the
Alex Loiko507e8d12018-02-27 12:51:47220// difference between input and output varies smoothly. Also check
221// that it is inside reasonable bounds. This is to catch issues like
222// chromium:695993 and chromium:816875.
aleloi2c9306e2017-03-29 11:25:16223TEST(FrameCombiner, GainCurveIsSmoothForAlternatingNumberOfStreams) {
Alex Loiko8396e342018-06-21 10:04:05224 // Rates are divisible by 2000 when limiter is active.
Alex Loiko507e8d12018-02-27 12:51:47225 std::vector<FrameCombinerConfig> configs = {
Alex Loiko8396e342018-06-21 10:04:05226 {false, 30100, 2, 50.f}, {false, 16500, 1, 3200.f},
227 {true, 8000, 1, 3200.f}, {true, 16000, 1, 50.f},
Alex Loikob4977de2019-01-28 15:38:38228 {true, 18000, 8, 3200.f}, {true, 10000, 2, 50.f},
Alex Loiko507e8d12018-02-27 12:51:47229 };
aleloi2c9306e2017-03-29 11:25:16230
Alex Loiko507e8d12018-02-27 12:51:47231 for (const auto& config : configs) {
232 SCOPED_TRACE(ProduceDebugText(config));
aleloi2c9306e2017-03-29 11:25:16233
Alex Loiko8396e342018-06-21 10:04:05234 FrameCombiner combiner(config.use_limiter);
aleloi2c9306e2017-03-29 11:25:16235
Alex Loiko507e8d12018-02-27 12:51:47236 constexpr int16_t wave_amplitude = 30000;
237 SineWaveGenerator wave_generator(config.wave_frequency, wave_amplitude);
aleloi2c9306e2017-03-29 11:25:16238
Alex Loiko507e8d12018-02-27 12:51:47239 GainChangeCalculator change_calculator;
240 float cumulative_change = 0.f;
aleloi2c9306e2017-03-29 11:25:16241
Alex Loiko507e8d12018-02-27 12:51:47242 constexpr size_t iterations = 100;
aleloi2c9306e2017-03-29 11:25:16243
Alex Loiko507e8d12018-02-27 12:51:47244 for (size_t i = 0; i < iterations; ++i) {
245 SetUpFrames(config.sample_rate_hz, config.number_of_channels);
246 wave_generator.GenerateNextFrame(&frame1);
247 AudioFrameOperations::Mute(&frame2);
aleloi2c9306e2017-03-29 11:25:16248
Alex Loiko507e8d12018-02-27 12:51:47249 std::vector<AudioFrame*> frames_to_combine = {&frame1};
250 if (i % 2 == 0) {
251 frames_to_combine.push_back(&frame2);
aleloi2c9306e2017-03-29 11:25:16252 }
Alex Loiko507e8d12018-02-27 12:51:47253 const size_t number_of_samples =
254 frame1.samples_per_channel_ * config.number_of_channels;
255
256 // Ensures limiter is on if 'use_limiter'.
257 constexpr size_t number_of_streams = 2;
258 combiner.Combine(frames_to_combine, config.number_of_channels,
259 config.sample_rate_hz, number_of_streams,
260 &audio_frame_for_mixing);
261 cumulative_change += change_calculator.CalculateGainChange(
262 rtc::ArrayView<const int16_t>(frame1.data(), number_of_samples),
263 rtc::ArrayView<const int16_t>(audio_frame_for_mixing.data(),
264 number_of_samples));
aleloi2c9306e2017-03-29 11:25:16265 }
Alex Loiko507e8d12018-02-27 12:51:47266
267 // Check that the gain doesn't vary too much.
268 EXPECT_LT(cumulative_change, 10);
269
270 // Check that the latest gain is within reasonable bounds. It
271 // should be slightly less that 1.
272 EXPECT_LT(0.9f, change_calculator.LatestGain());
273 EXPECT_LT(change_calculator.LatestGain(), 1.01f);
aleloi2c9306e2017-03-29 11:25:16274 }
275}
aleloi24899e52017-02-21 13:06:29276} // namespace webrtc