blob: 490e99e495401ae4329ffea04db098ea7df074fd [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>
14#include <sstream>
15#include <string>
16
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "audio/utility/audio_frame_operations.h"
18#include "modules/audio_mixer/gain_change_calculator.h"
19#include "modules/audio_mixer/sine_wave_generator.h"
20#include "rtc_base/checks.h"
21#include "test/gtest.h"
aleloi24899e52017-02-21 05:06:29 -080022
23namespace webrtc {
24
25namespace {
26std::string ProduceDebugText(int sample_rate_hz,
27 int number_of_channels,
28 int number_of_sources) {
29 std::ostringstream ss;
aleloi2c9306e2017-03-29 04:25:16 -070030 ss << "Sample rate: " << sample_rate_hz << " ,";
31 ss << "number of channels: " << number_of_channels << " ,";
32 ss << "number of sources: " << number_of_sources;
33 return ss.str();
34}
35
36std::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 ? "true" : "false") << " ,";
46 ss << "wave frequency: " << wave_frequency << " ,";
aleloi24899e52017-02-21 05:06:29 -080047 return ss.str();
48}
49
50AudioFrame frame1;
51AudioFrame frame2;
52AudioFrame audio_frame_for_mixing;
53
54void SetUpFrames(int sample_rate_hz, int number_of_channels) {
55 for (auto* frame : {&frame1, &frame2}) {
solenbergc7b4a452017-09-28 07:37:11 -070056 frame->UpdateFrame(0, nullptr, rtc::CheckedDivExact(sample_rate_hz, 100),
aleloi24899e52017-02-21 05:06:29 -080057 sample_rate_hz, AudioFrame::kNormalSpeech,
58 AudioFrame::kVadActive, number_of_channels);
59 }
60}
61} // namespace
62
63TEST(FrameCombiner, BasicApiCallsLimiter) {
64 FrameCombiner combiner(true);
65 for (const int rate : {8000, 16000, 32000, 48000}) {
66 for (const int number_of_channels : {1, 2}) {
67 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
68 SetUpFrames(rate, number_of_channels);
69
70 for (const int number_of_frames : {0, 1, 2}) {
71 SCOPED_TRACE(
72 ProduceDebugText(rate, number_of_channels, number_of_frames));
73 const std::vector<AudioFrame*> frames_to_combine(
74 all_frames.begin(), all_frames.begin() + number_of_frames);
75 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -070076 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -080077 }
78 }
79 }
80}
81
82// No APM limiter means no AudioProcessing::NativeRate restriction
83// on rate. The rate has to be divisible by 100 since we use
84// 10 ms frames, though.
85TEST(FrameCombiner, BasicApiCallsNoLimiter) {
86 FrameCombiner combiner(false);
87 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
88 for (const int number_of_channels : {1, 2}) {
89 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
90 SetUpFrames(rate, number_of_channels);
91
92 for (const int number_of_frames : {0, 1, 2}) {
93 SCOPED_TRACE(
94 ProduceDebugText(rate, number_of_channels, number_of_frames));
95 const std::vector<AudioFrame*> frames_to_combine(
96 all_frames.begin(), all_frames.begin() + number_of_frames);
97 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -070098 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -080099 }
100 }
101 }
102}
103
104TEST(FrameCombiner, CombiningZeroFramesShouldProduceSilence) {
105 FrameCombiner combiner(false);
106 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
107 for (const int number_of_channels : {1, 2}) {
108 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 0));
109
110 const std::vector<AudioFrame*> frames_to_combine;
111 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700112 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800113
yujo36b1a5f2017-06-12 12:45:32 -0700114 const int16_t* audio_frame_for_mixing_data =
115 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 05:06:29 -0800116 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 12:45:32 -0700117 audio_frame_for_mixing_data,
118 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 05:06:29 -0800119
120 const std::vector<int16_t> expected(number_of_channels * rate / 100, 0);
121 EXPECT_EQ(mixed_data, expected);
122 }
123 }
124}
125
126TEST(FrameCombiner, CombiningOneFrameShouldNotChangeFrame) {
127 FrameCombiner combiner(false);
128 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
129 for (const int number_of_channels : {1, 2}) {
130 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1));
131
132 SetUpFrames(rate, number_of_channels);
yujo36b1a5f2017-06-12 12:45:32 -0700133 int16_t* frame1_data = frame1.mutable_data();
134 std::iota(frame1_data, frame1_data + number_of_channels * rate / 100, 0);
aleloi24899e52017-02-21 05:06:29 -0800135 const std::vector<AudioFrame*> frames_to_combine = {&frame1};
136 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700137 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800138
yujo36b1a5f2017-06-12 12:45:32 -0700139 const int16_t* audio_frame_for_mixing_data =
140 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 05:06:29 -0800141 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 12:45:32 -0700142 audio_frame_for_mixing_data,
143 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 05:06:29 -0800144
145 std::vector<int16_t> expected(number_of_channels * rate / 100);
146 std::iota(expected.begin(), expected.end(), 0);
147 EXPECT_EQ(mixed_data, expected);
148 }
149 }
150}
151
aleloi2c9306e2017-03-29 04:25:16 -0700152// Send a sine wave through the FrameCombiner, and check that the
153// difference between input and output varies smoothly. This is to
154// catch issues like chromium:695993.
155TEST(FrameCombiner, GainCurveIsSmoothForAlternatingNumberOfStreams) {
156 // Test doesn't work with rates requiring a band split, because it
157 // introduces a small delay measured in single samples, and this
158 // test cannot handle it.
159 //
160 // TODO(aleloi): Add more rates when APM limiter doesn't use band
161 // split.
162 for (const bool use_limiter : {true, false}) {
163 for (const int rate : {8000, 16000}) {
164 constexpr int number_of_channels = 2;
165 for (const float wave_frequency : {50, 400, 3200}) {
166 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1, use_limiter,
167 wave_frequency));
168
169 FrameCombiner combiner(use_limiter);
170
171 constexpr int16_t wave_amplitude = 30000;
172 SineWaveGenerator wave_generator(wave_frequency, wave_amplitude);
173
174 GainChangeCalculator change_calculator;
175 float cumulative_change = 0.f;
176
177 constexpr size_t iterations = 100;
178
179 for (size_t i = 0; i < iterations; ++i) {
180 SetUpFrames(rate, number_of_channels);
181 wave_generator.GenerateNextFrame(&frame1);
182 AudioFrameOperations::Mute(&frame2);
183
184 std::vector<AudioFrame*> frames_to_combine = {&frame1};
185 if (i % 2 == 0) {
186 frames_to_combine.push_back(&frame2);
187 }
188 const size_t number_of_samples =
189 frame1.samples_per_channel_ * number_of_channels;
190
191 // Ensures limiter is on if 'use_limiter'.
192 constexpr size_t number_of_streams = 2;
193 combiner.Combine(frames_to_combine, number_of_channels, rate,
194 number_of_streams, &audio_frame_for_mixing);
195 cumulative_change += change_calculator.CalculateGainChange(
yujo36b1a5f2017-06-12 12:45:32 -0700196 rtc::ArrayView<const int16_t>(frame1.data(), number_of_samples),
197 rtc::ArrayView<const int16_t>(audio_frame_for_mixing.data(),
aleloi2c9306e2017-03-29 04:25:16 -0700198 number_of_samples));
199 }
200 RTC_DCHECK_LT(cumulative_change, 10);
201 }
202 }
203 }
204}
aleloi24899e52017-02-21 05:06:29 -0800205} // namespace webrtc