blob: 45d8a4e912b4037a43d74c56d7f837c9bd9262aa [file] [log] [blame]
aleloi24899e52017-02-21 05:06:29 -08001/*
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 06:47:31 +020011#include "modules/audio_mixer/frame_combiner.h"
aleloi24899e52017-02-21 05:06:29 -080012
13#include <numeric>
aleloi24899e52017-02-21 05:06:29 -080014#include <string>
15
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "audio/utility/audio_frame_operations.h"
17#include "modules/audio_mixer/gain_change_calculator.h"
18#include "modules/audio_mixer/sine_wave_generator.h"
19#include "rtc_base/checks.h"
Jonas Olsson366a50c2018-09-06 13:41:30 +020020#include "rtc_base/strings/string_builder.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "test/gtest.h"
aleloi24899e52017-02-21 05:06:29 -080022
23namespace webrtc {
24
25namespace {
Alex Loiko507e8d12018-02-27 13:51:47 +010026using LimiterType = FrameCombiner::LimiterType;
Alex Loiko507e8d12018-02-27 13:51:47 +010027struct FrameCombinerConfig {
Alex Loiko8396e342018-06-21 12:04:05 +020028 bool use_limiter;
29 int sample_rate_hz;
Alex Loiko507e8d12018-02-27 13:51:47 +010030 int number_of_channels;
31 float wave_frequency;
32};
33
aleloi24899e52017-02-21 05:06:29 -080034std::string ProduceDebugText(int sample_rate_hz,
35 int number_of_channels,
36 int number_of_sources) {
Jonas Olsson366a50c2018-09-06 13:41:30 +020037 rtc::StringBuilder ss;
aleloi2c9306e2017-03-29 04:25:16 -070038 ss << "Sample rate: " << sample_rate_hz << " ,";
39 ss << "number of channels: " << number_of_channels << " ,";
40 ss << "number of sources: " << number_of_sources;
Jonas Olsson84df1c72018-09-14 16:59:32 +020041 return ss.Release();
aleloi2c9306e2017-03-29 04:25:16 -070042}
43
Alex Loiko507e8d12018-02-27 13:51:47 +010044std::string ProduceDebugText(const FrameCombinerConfig& config) {
Jonas Olsson366a50c2018-09-06 13:41:30 +020045 rtc::StringBuilder ss;
Alex Loiko507e8d12018-02-27 13:51:47 +010046 ss << "Sample rate: " << config.sample_rate_hz << " ,";
47 ss << "number of channels: " << config.number_of_channels << " ,";
Alex Loiko8396e342018-06-21 12:04:05 +020048 ss << "limiter active: " << (config.use_limiter ? "on" : "off") << " ,";
Alex Loiko507e8d12018-02-27 13:51:47 +010049 ss << "wave frequency: " << config.wave_frequency << " ,";
Jonas Olsson84df1c72018-09-14 16:59:32 +020050 return ss.Release();
aleloi24899e52017-02-21 05:06:29 -080051}
52
53AudioFrame frame1;
54AudioFrame frame2;
55AudioFrame audio_frame_for_mixing;
56
57void SetUpFrames(int sample_rate_hz, int number_of_channels) {
58 for (auto* frame : {&frame1, &frame2}) {
solenbergc7b4a452017-09-28 07:37:11 -070059 frame->UpdateFrame(0, nullptr, rtc::CheckedDivExact(sample_rate_hz, 100),
aleloi24899e52017-02-21 05:06:29 -080060 sample_rate_hz, AudioFrame::kNormalSpeech,
61 AudioFrame::kVadActive, number_of_channels);
62 }
63}
64} // namespace
65
Alex Loiko8396e342018-06-21 12:04:05 +020066// The limiter requires sample rate divisible by 2000.
aleloi24899e52017-02-21 05:06:29 -080067TEST(FrameCombiner, BasicApiCallsLimiter) {
Alex Loiko8396e342018-06-21 12:04:05 +020068 FrameCombiner combiner(true);
69 for (const int rate : {8000, 18000, 34000, 48000}) {
aleloi24899e52017-02-21 05:06:29 -080070 for (const int number_of_channels : {1, 2}) {
71 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
72 SetUpFrames(rate, number_of_channels);
73
74 for (const int number_of_frames : {0, 1, 2}) {
75 SCOPED_TRACE(
76 ProduceDebugText(rate, number_of_channels, number_of_frames));
77 const std::vector<AudioFrame*> frames_to_combine(
78 all_frames.begin(), all_frames.begin() + number_of_frames);
79 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -070080 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -080081 }
82 }
83 }
84}
85
Alex Loiko8396e342018-06-21 12:04:05 +020086// With no limiter, the rate has to be divisible by 100 since we use
87// 10 ms frames.
aleloi24899e52017-02-21 05:06:29 -080088TEST(FrameCombiner, BasicApiCallsNoLimiter) {
Alex Loiko8396e342018-06-21 12:04:05 +020089 FrameCombiner combiner(false);
aleloi24899e52017-02-21 05:06:29 -080090 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
91 for (const int number_of_channels : {1, 2}) {
92 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
93 SetUpFrames(rate, number_of_channels);
94
95 for (const int number_of_frames : {0, 1, 2}) {
96 SCOPED_TRACE(
97 ProduceDebugText(rate, number_of_channels, number_of_frames));
98 const std::vector<AudioFrame*> frames_to_combine(
99 all_frames.begin(), all_frames.begin() + number_of_frames);
100 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700101 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800102 }
103 }
104 }
105}
106
107TEST(FrameCombiner, CombiningZeroFramesShouldProduceSilence) {
Alex Loiko8396e342018-06-21 12:04:05 +0200108 FrameCombiner combiner(false);
aleloi24899e52017-02-21 05:06:29 -0800109 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
110 for (const int number_of_channels : {1, 2}) {
111 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 0));
112
113 const std::vector<AudioFrame*> frames_to_combine;
114 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700115 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800116
yujo36b1a5f2017-06-12 12:45:32 -0700117 const int16_t* audio_frame_for_mixing_data =
118 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 05:06:29 -0800119 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 12:45:32 -0700120 audio_frame_for_mixing_data,
121 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 05:06:29 -0800122
123 const std::vector<int16_t> expected(number_of_channels * rate / 100, 0);
124 EXPECT_EQ(mixed_data, expected);
125 }
126 }
127}
128
129TEST(FrameCombiner, CombiningOneFrameShouldNotChangeFrame) {
Alex Loiko8396e342018-06-21 12:04:05 +0200130 FrameCombiner combiner(false);
aleloi24899e52017-02-21 05:06:29 -0800131 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
132 for (const int number_of_channels : {1, 2}) {
133 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1));
134
135 SetUpFrames(rate, number_of_channels);
yujo36b1a5f2017-06-12 12:45:32 -0700136 int16_t* frame1_data = frame1.mutable_data();
137 std::iota(frame1_data, frame1_data + number_of_channels * rate / 100, 0);
aleloi24899e52017-02-21 05:06:29 -0800138 const std::vector<AudioFrame*> frames_to_combine = {&frame1};
139 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700140 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800141
yujo36b1a5f2017-06-12 12:45:32 -0700142 const int16_t* audio_frame_for_mixing_data =
143 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 05:06:29 -0800144 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 12:45:32 -0700145 audio_frame_for_mixing_data,
146 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 05:06:29 -0800147
148 std::vector<int16_t> expected(number_of_channels * rate / 100);
149 std::iota(expected.begin(), expected.end(), 0);
150 EXPECT_EQ(mixed_data, expected);
151 }
152 }
153}
154
aleloi2c9306e2017-03-29 04:25:16 -0700155// Send a sine wave through the FrameCombiner, and check that the
Alex Loiko507e8d12018-02-27 13:51:47 +0100156// difference between input and output varies smoothly. Also check
157// that it is inside reasonable bounds. This is to catch issues like
158// chromium:695993 and chromium:816875.
aleloi2c9306e2017-03-29 04:25:16 -0700159TEST(FrameCombiner, GainCurveIsSmoothForAlternatingNumberOfStreams) {
Alex Loiko8396e342018-06-21 12:04:05 +0200160 // Rates are divisible by 2000 when limiter is active.
Alex Loiko507e8d12018-02-27 13:51:47 +0100161 std::vector<FrameCombinerConfig> configs = {
Alex Loiko8396e342018-06-21 12:04:05 +0200162 {false, 30100, 2, 50.f}, {false, 16500, 1, 3200.f},
163 {true, 8000, 1, 3200.f}, {true, 16000, 1, 50.f},
164 {true, 18000, 2, 3200.f}, {true, 10000, 2, 50.f},
Alex Loiko507e8d12018-02-27 13:51:47 +0100165 };
aleloi2c9306e2017-03-29 04:25:16 -0700166
Alex Loiko507e8d12018-02-27 13:51:47 +0100167 for (const auto& config : configs) {
168 SCOPED_TRACE(ProduceDebugText(config));
aleloi2c9306e2017-03-29 04:25:16 -0700169
Alex Loiko8396e342018-06-21 12:04:05 +0200170 FrameCombiner combiner(config.use_limiter);
aleloi2c9306e2017-03-29 04:25:16 -0700171
Alex Loiko507e8d12018-02-27 13:51:47 +0100172 constexpr int16_t wave_amplitude = 30000;
173 SineWaveGenerator wave_generator(config.wave_frequency, wave_amplitude);
aleloi2c9306e2017-03-29 04:25:16 -0700174
Alex Loiko507e8d12018-02-27 13:51:47 +0100175 GainChangeCalculator change_calculator;
176 float cumulative_change = 0.f;
aleloi2c9306e2017-03-29 04:25:16 -0700177
Alex Loiko507e8d12018-02-27 13:51:47 +0100178 constexpr size_t iterations = 100;
aleloi2c9306e2017-03-29 04:25:16 -0700179
Alex Loiko507e8d12018-02-27 13:51:47 +0100180 for (size_t i = 0; i < iterations; ++i) {
181 SetUpFrames(config.sample_rate_hz, config.number_of_channels);
182 wave_generator.GenerateNextFrame(&frame1);
183 AudioFrameOperations::Mute(&frame2);
aleloi2c9306e2017-03-29 04:25:16 -0700184
Alex Loiko507e8d12018-02-27 13:51:47 +0100185 std::vector<AudioFrame*> frames_to_combine = {&frame1};
186 if (i % 2 == 0) {
187 frames_to_combine.push_back(&frame2);
aleloi2c9306e2017-03-29 04:25:16 -0700188 }
Alex Loiko507e8d12018-02-27 13:51:47 +0100189 const size_t number_of_samples =
190 frame1.samples_per_channel_ * config.number_of_channels;
191
192 // Ensures limiter is on if 'use_limiter'.
193 constexpr size_t number_of_streams = 2;
194 combiner.Combine(frames_to_combine, config.number_of_channels,
195 config.sample_rate_hz, number_of_streams,
196 &audio_frame_for_mixing);
197 cumulative_change += change_calculator.CalculateGainChange(
198 rtc::ArrayView<const int16_t>(frame1.data(), number_of_samples),
199 rtc::ArrayView<const int16_t>(audio_frame_for_mixing.data(),
200 number_of_samples));
aleloi2c9306e2017-03-29 04:25:16 -0700201 }
Alex Loiko507e8d12018-02-27 13:51:47 +0100202
203 // Check that the gain doesn't vary too much.
204 EXPECT_LT(cumulative_change, 10);
205
206 // Check that the latest gain is within reasonable bounds. It
207 // should be slightly less that 1.
208 EXPECT_LT(0.9f, change_calculator.LatestGain());
209 EXPECT_LT(change_calculator.LatestGain(), 1.01f);
aleloi2c9306e2017-03-29 04:25:16 -0700210 }
211}
aleloi24899e52017-02-21 05:06:29 -0800212} // namespace webrtc