/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/modules/audio_processing/agc/agc.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "webrtc/modules/include/module_common_types.h" #include "webrtc/test/testsupport/fileutils.h" #include "webrtc/tools/agc/test_utils.h" using ::testing::_; using ::testing::AllOf; using ::testing::AtLeast; using ::testing::Eq; using ::testing::Gt; using ::testing::InSequence; using ::testing::Lt; using ::testing::Mock; using ::testing::SaveArg; namespace webrtc { namespace { // The tested values depend on this assumed gain. const int kMaxGain = 80; MATCHER_P(GtPointee, p, "") { return arg > *p; } MATCHER_P(LtPointee, p, "") { return arg < *p; } class AgcChecker { public: MOCK_METHOD2(LevelChanged, void(int iterations, int level)); }; class AgcTest : public ::testing::Test { protected: AgcTest() : agc_(), checker_(), mic_level_(128) { } // A gain of <= -100 will zero out the signal. void RunAgc(int iterations, float gain_db) { FILE* input_file = fopen( test::ResourcePath("voice_engine/audio_long16", "pcm").c_str(), "rb"); ASSERT_TRUE(input_file != NULL); AudioFrame frame; frame.sample_rate_hz_ = 16000; frame.num_channels_ = 1; frame.samples_per_channel_ = frame.sample_rate_hz_ / 100; const size_t length = frame.samples_per_channel_ * frame.num_channels_; float gain = Db2Linear(gain_db); if (gain_db <= -100) { gain = 0; } for (int i = 0; i < iterations; ++i) { ASSERT_EQ(length, fread(frame.data_, sizeof(int16_t), length, input_file)); SimulateMic(kMaxGain, mic_level_, &frame); ApplyGainLinear(gain, &frame); ASSERT_GE(agc_.Process(frame), 0); int mic_level = agc_.MicLevel(); if (mic_level != mic_level_) { printf("mic_level=%d\n", mic_level); checker_.LevelChanged(i, mic_level); } mic_level_ = mic_level; } fclose(input_file); } Agc agc_; AgcChecker checker_; // Stores mic level between multiple runs of RunAgc in one test. int mic_level_; }; TEST_F(AgcTest, UpwardsChangeIsLimited) { { InSequence seq; EXPECT_CALL(checker_, LevelChanged(Lt(500), Eq(179))).Times(1); EXPECT_CALL(checker_, LevelChanged(_, Gt(179))).Times(AtLeast(1)); } RunAgc(1000, -40); } TEST_F(AgcTest, DownwardsChangeIsLimited) { { InSequence seq; EXPECT_CALL(checker_, LevelChanged(Lt(500), Eq(77))).Times(1); EXPECT_CALL(checker_, LevelChanged(_, Lt(77))).Times(AtLeast(1)); } RunAgc(1000, 40); } TEST_F(AgcTest, MovesUpToMaxAndDownToMin) { int last_level = 128; EXPECT_CALL(checker_, LevelChanged(_, GtPointee(&last_level))) .Times(AtLeast(2)) .WillRepeatedly(SaveArg<1>(&last_level)); RunAgc(1000, -30); EXPECT_EQ(255, last_level); Mock::VerifyAndClearExpectations(&checker_); EXPECT_CALL(checker_, LevelChanged(_, LtPointee(&last_level))) .Times(AtLeast(2)) .WillRepeatedly(SaveArg<1>(&last_level)); RunAgc(1000, 50); EXPECT_EQ(1, last_level); } TEST_F(AgcTest, HandlesZeroSignal) { int last_level = 128; // Doesn't respond to a zero signal. EXPECT_CALL(checker_, LevelChanged(_, _)).Times(0); RunAgc(1000, -100); Mock::VerifyAndClearExpectations(&checker_); // Reacts as usual afterwards. EXPECT_CALL(checker_, LevelChanged(_, GtPointee(&last_level))) .Times(AtLeast(2)) .WillRepeatedly(SaveArg<1>(&last_level)); RunAgc(500, -20); } TEST_F(AgcTest, ReachesSteadyState) { int last_level = 128; EXPECT_CALL(checker_, LevelChanged(_, _)) .Times(AtLeast(2)) .WillRepeatedly(SaveArg<1>(&last_level)); RunAgc(1000, -20); Mock::VerifyAndClearExpectations(&checker_); // If the level changes, it should be in a narrow band around the previous // adaptation. EXPECT_CALL(checker_, LevelChanged(_, AllOf(Gt(last_level * 0.95), Lt(last_level * 1.05)))) .Times(AtLeast(0)); RunAgc(1000, -20); } // TODO(ajm): Add this test; requires measuring the signal RMS. TEST_F(AgcTest, AdaptsToCorrectRMS) { } } // namespace } // namespace webrtc