blob: 250c6e1954a1567c1a40b91290bc99664827f425 [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
11#include "webrtc/modules/audio_mixer/frame_combiner.h"
12
13#include <numeric>
14#include <sstream>
15#include <string>
16
aleloi2c9306e2017-03-29 04:25:16 -070017#include "webrtc/audio/utility/audio_frame_operations.h"
aleloi24899e52017-02-21 05:06:29 -080018#include "webrtc/base/checks.h"
aleloi2c9306e2017-03-29 04:25:16 -070019#include "webrtc/modules/audio_mixer/gain_change_calculator.h"
20#include "webrtc/modules/audio_mixer/sine_wave_generator.h"
aleloi24899e52017-02-21 05:06:29 -080021#include "webrtc/test/gtest.h"
22
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}) {
56 frame->UpdateFrame(-1, 0, nullptr,
57 rtc::CheckedDivExact(sample_rate_hz, 100),
58 sample_rate_hz, AudioFrame::kNormalSpeech,
59 AudioFrame::kVadActive, number_of_channels);
60 }
61}
62} // namespace
63
64TEST(FrameCombiner, BasicApiCallsLimiter) {
65 FrameCombiner combiner(true);
66 for (const int rate : {8000, 16000, 32000, 48000}) {
67 for (const int number_of_channels : {1, 2}) {
68 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
69 SetUpFrames(rate, number_of_channels);
70
71 for (const int number_of_frames : {0, 1, 2}) {
72 SCOPED_TRACE(
73 ProduceDebugText(rate, number_of_channels, number_of_frames));
74 const std::vector<AudioFrame*> frames_to_combine(
75 all_frames.begin(), all_frames.begin() + number_of_frames);
76 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -070077 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -080078 }
79 }
80 }
81}
82
83// No APM limiter means no AudioProcessing::NativeRate restriction
84// on rate. The rate has to be divisible by 100 since we use
85// 10 ms frames, though.
86TEST(FrameCombiner, BasicApiCallsNoLimiter) {
87 FrameCombiner combiner(false);
88 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
89 for (const int number_of_channels : {1, 2}) {
90 const std::vector<AudioFrame*> all_frames = {&frame1, &frame2};
91 SetUpFrames(rate, number_of_channels);
92
93 for (const int number_of_frames : {0, 1, 2}) {
94 SCOPED_TRACE(
95 ProduceDebugText(rate, number_of_channels, number_of_frames));
96 const std::vector<AudioFrame*> frames_to_combine(
97 all_frames.begin(), all_frames.begin() + number_of_frames);
98 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -070099 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800100 }
101 }
102 }
103}
104
105TEST(FrameCombiner, CombiningZeroFramesShouldProduceSilence) {
106 FrameCombiner combiner(false);
107 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
108 for (const int number_of_channels : {1, 2}) {
109 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 0));
110
111 const std::vector<AudioFrame*> frames_to_combine;
112 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700113 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800114
yujo36b1a5f2017-06-12 12:45:32 -0700115 const int16_t* audio_frame_for_mixing_data =
116 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 05:06:29 -0800117 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 12:45:32 -0700118 audio_frame_for_mixing_data,
119 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 05:06:29 -0800120
121 const std::vector<int16_t> expected(number_of_channels * rate / 100, 0);
122 EXPECT_EQ(mixed_data, expected);
123 }
124 }
125}
126
127TEST(FrameCombiner, CombiningOneFrameShouldNotChangeFrame) {
128 FrameCombiner combiner(false);
129 for (const int rate : {8000, 10000, 11000, 32000, 44100}) {
130 for (const int number_of_channels : {1, 2}) {
131 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1));
132
133 SetUpFrames(rate, number_of_channels);
yujo36b1a5f2017-06-12 12:45:32 -0700134 int16_t* frame1_data = frame1.mutable_data();
135 std::iota(frame1_data, frame1_data + number_of_channels * rate / 100, 0);
aleloi24899e52017-02-21 05:06:29 -0800136 const std::vector<AudioFrame*> frames_to_combine = {&frame1};
137 combiner.Combine(frames_to_combine, number_of_channels, rate,
aleloi2c9306e2017-03-29 04:25:16 -0700138 frames_to_combine.size(), &audio_frame_for_mixing);
aleloi24899e52017-02-21 05:06:29 -0800139
yujo36b1a5f2017-06-12 12:45:32 -0700140 const int16_t* audio_frame_for_mixing_data =
141 audio_frame_for_mixing.data();
aleloi24899e52017-02-21 05:06:29 -0800142 const std::vector<int16_t> mixed_data(
yujo36b1a5f2017-06-12 12:45:32 -0700143 audio_frame_for_mixing_data,
144 audio_frame_for_mixing_data + number_of_channels * rate / 100);
aleloi24899e52017-02-21 05:06:29 -0800145
146 std::vector<int16_t> expected(number_of_channels * rate / 100);
147 std::iota(expected.begin(), expected.end(), 0);
148 EXPECT_EQ(mixed_data, expected);
149 }
150 }
151}
152
aleloi2c9306e2017-03-29 04:25:16 -0700153// Send a sine wave through the FrameCombiner, and check that the
154// difference between input and output varies smoothly. This is to
155// catch issues like chromium:695993.
156TEST(FrameCombiner, GainCurveIsSmoothForAlternatingNumberOfStreams) {
157 // Test doesn't work with rates requiring a band split, because it
158 // introduces a small delay measured in single samples, and this
159 // test cannot handle it.
160 //
161 // TODO(aleloi): Add more rates when APM limiter doesn't use band
162 // split.
163 for (const bool use_limiter : {true, false}) {
164 for (const int rate : {8000, 16000}) {
165 constexpr int number_of_channels = 2;
166 for (const float wave_frequency : {50, 400, 3200}) {
167 SCOPED_TRACE(ProduceDebugText(rate, number_of_channels, 1, use_limiter,
168 wave_frequency));
169
170 FrameCombiner combiner(use_limiter);
171
172 constexpr int16_t wave_amplitude = 30000;
173 SineWaveGenerator wave_generator(wave_frequency, wave_amplitude);
174
175 GainChangeCalculator change_calculator;
176 float cumulative_change = 0.f;
177
178 constexpr size_t iterations = 100;
179
180 for (size_t i = 0; i < iterations; ++i) {
181 SetUpFrames(rate, number_of_channels);
182 wave_generator.GenerateNextFrame(&frame1);
183 AudioFrameOperations::Mute(&frame2);
184
185 std::vector<AudioFrame*> frames_to_combine = {&frame1};
186 if (i % 2 == 0) {
187 frames_to_combine.push_back(&frame2);
188 }
189 const size_t number_of_samples =
190 frame1.samples_per_channel_ * 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, number_of_channels, rate,
195 number_of_streams, &audio_frame_for_mixing);
196 cumulative_change += change_calculator.CalculateGainChange(
yujo36b1a5f2017-06-12 12:45:32 -0700197 rtc::ArrayView<const int16_t>(frame1.data(), number_of_samples),
198 rtc::ArrayView<const int16_t>(audio_frame_for_mixing.data(),
aleloi2c9306e2017-03-29 04:25:16 -0700199 number_of_samples));
200 }
201 RTC_DCHECK_LT(cumulative_change, 10);
202 }
203 }
204 }
205}
aleloi24899e52017-02-21 05:06:29 -0800206} // namespace webrtc