| /* | 
 |  *  Copyright (c) 2016 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/aec3/echo_canceller3.h" | 
 |  | 
 | #include <deque> | 
 | #include <memory> | 
 | #include <sstream> | 
 | #include <string> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "webrtc/modules/audio_processing/aec3/aec3_common.h" | 
 | #include "webrtc/modules/audio_processing/aec3/block_processor.h" | 
 | #include "webrtc/modules/audio_processing/aec3/frame_blocker.h" | 
 | #include "webrtc/modules/audio_processing/aec3/mock/mock_block_processor.h" | 
 | #include "webrtc/modules/audio_processing/audio_buffer.h" | 
 | #include "webrtc/test/gmock.h" | 
 | #include "webrtc/test/gtest.h" | 
 |  | 
 | namespace webrtc { | 
 | namespace { | 
 |  | 
 | using testing::Return; | 
 | using testing::StrictMock; | 
 | using testing::_; | 
 |  | 
 | // Populates the frame with linearly increasing sample values for each band, | 
 | // with a band-specific offset, in order to allow simple bitexactness | 
 | // verification for each band. | 
 | void PopulateInputFrame(size_t frame_length, | 
 |                         size_t num_bands, | 
 |                         size_t frame_index, | 
 |                         float* const* frame, | 
 |                         int offset) { | 
 |   for (size_t k = 0; k < num_bands; ++k) { | 
 |     for (size_t i = 0; i < frame_length; ++i) { | 
 |       float value = static_cast<int>(frame_index * frame_length + i) + offset; | 
 |       frame[k][i] = (value > 0 ? 5000 * k + value : 0); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | // Populates the frame with linearly increasing sample values. | 
 | void PopulateInputFrame(size_t frame_length, | 
 |                         size_t frame_index, | 
 |                         float* frame, | 
 |                         int offset) { | 
 |   for (size_t i = 0; i < frame_length; ++i) { | 
 |     float value = static_cast<int>(frame_index * frame_length + i) + offset; | 
 |     frame[i] = std::max(value, 0.f); | 
 |   } | 
 | } | 
 |  | 
 | // Verifies the that samples in the output frame are identical to the samples | 
 | // that were produced for the input frame, with an offset in order to compensate | 
 | // for buffering delays. | 
 | bool VerifyOutputFrameBitexactness(size_t frame_length, | 
 |                                    size_t num_bands, | 
 |                                    size_t frame_index, | 
 |                                    const float* const* frame, | 
 |                                    int offset) { | 
 |   float reference_frame_data[kMaxNumBands][2 * kSubFrameLength]; | 
 |   float* reference_frame[kMaxNumBands]; | 
 |   for (size_t k = 0; k < num_bands; ++k) { | 
 |     reference_frame[k] = &reference_frame_data[k][0]; | 
 |   } | 
 |  | 
 |   PopulateInputFrame(frame_length, num_bands, frame_index, reference_frame, | 
 |                      offset); | 
 |   for (size_t k = 0; k < num_bands; ++k) { | 
 |     for (size_t i = 0; i < frame_length; ++i) { | 
 |       if (reference_frame[k][i] != frame[k][i]) { | 
 |         return false; | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | // Verifies the that samples in the output frame are identical to the samples | 
 | // that were produced for the input frame, with an offset in order to compensate | 
 | // for buffering delays. | 
 | bool VerifyOutputFrameBitexactness(size_t frame_length, | 
 |                                    size_t frame_index, | 
 |                                    const float* const* frame, | 
 |                                    int offset) { | 
 |   float reference_frame[480]; | 
 |  | 
 |   PopulateInputFrame(frame_length, frame_index, reference_frame, offset); | 
 |   for (size_t i = 0; i < frame_length; ++i) { | 
 |     if (reference_frame[i] != frame[0][i]) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | // Class for testing that the capture data is properly received by the block | 
 | // processor and that the processor data is properly passed to the | 
 | // EchoCanceller3 output. | 
 | class CaptureTransportVerificationProcessor : public BlockProcessor { | 
 |  public: | 
 |   explicit CaptureTransportVerificationProcessor(size_t num_bands) {} | 
 |   ~CaptureTransportVerificationProcessor() override = default; | 
 |  | 
 |   void ProcessCapture(bool level_change, | 
 |                       bool saturated_microphone_signal, | 
 |                       std::vector<std::vector<float>>* capture_block) override { | 
 |   } | 
 |  | 
 |   bool BufferRender(std::vector<std::vector<float>>* block) override { | 
 |     return true; | 
 |   } | 
 |  | 
 |   void UpdateEchoLeakageStatus(bool leakage_detected) override {} | 
 |  | 
 |  private: | 
 |   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(CaptureTransportVerificationProcessor); | 
 | }; | 
 |  | 
 | // Class for testing that the render data is properly received by the block | 
 | // processor. | 
 | class RenderTransportVerificationProcessor : public BlockProcessor { | 
 |  public: | 
 |   explicit RenderTransportVerificationProcessor(size_t num_bands) {} | 
 |   ~RenderTransportVerificationProcessor() override = default; | 
 |  | 
 |   void ProcessCapture(bool level_change, | 
 |                       bool saturated_microphone_signal, | 
 |                       std::vector<std::vector<float>>* capture_block) override { | 
 |     std::vector<std::vector<float>> render_block = | 
 |         received_render_blocks_.front(); | 
 |     received_render_blocks_.pop_front(); | 
 |     capture_block->swap(render_block); | 
 |   } | 
 |  | 
 |   bool BufferRender(std::vector<std::vector<float>>* block) override { | 
 |     received_render_blocks_.push_back(*block); | 
 |     return true; | 
 |   } | 
 |  | 
 |   void UpdateEchoLeakageStatus(bool leakage_detected) override {} | 
 |  | 
 |  private: | 
 |   std::deque<std::vector<std::vector<float>>> received_render_blocks_; | 
 |   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RenderTransportVerificationProcessor); | 
 | }; | 
 |  | 
 | class EchoCanceller3Tester { | 
 |  public: | 
 |   explicit EchoCanceller3Tester(int sample_rate_hz) | 
 |       : sample_rate_hz_(sample_rate_hz), | 
 |         num_bands_(NumBandsForRate(sample_rate_hz_)), | 
 |         frame_length_(sample_rate_hz_ == 8000 ? 80 : 160), | 
 |         fullband_frame_length_(rtc::CheckedDivExact(sample_rate_hz_, 100)), | 
 |         capture_buffer_(fullband_frame_length_, | 
 |                         1, | 
 |                         fullband_frame_length_, | 
 |                         1, | 
 |                         fullband_frame_length_), | 
 |         render_buffer_(fullband_frame_length_, | 
 |                        1, | 
 |                        fullband_frame_length_, | 
 |                        1, | 
 |                        fullband_frame_length_) {} | 
 |  | 
 |   // Verifies that the capture data is properly received by the block processor | 
 |   // and that the processor data is properly passed to the EchoCanceller3 | 
 |   // output. | 
 |   void RunCaptureTransportVerificationTest() { | 
 |     EchoCanceller3 aec3( | 
 |         sample_rate_hz_, false, | 
 |         std::unique_ptr<BlockProcessor>( | 
 |             new CaptureTransportVerificationProcessor(num_bands_))); | 
 |  | 
 |     for (size_t frame_index = 0; frame_index < kNumFramesToProcess; | 
 |          ++frame_index) { | 
 |       aec3.AnalyzeCapture(&capture_buffer_); | 
 |       OptionalBandSplit(); | 
 |       PopulateInputFrame(frame_length_, num_bands_, frame_index, | 
 |                          &capture_buffer_.split_bands_f(0)[0], 0); | 
 |       PopulateInputFrame(frame_length_, frame_index, | 
 |                          &render_buffer_.channels_f()[0][0], 0); | 
 |  | 
 |       EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer_)); | 
 |       aec3.ProcessCapture(&capture_buffer_, false); | 
 |       EXPECT_TRUE(VerifyOutputFrameBitexactness( | 
 |           frame_length_, num_bands_, frame_index, | 
 |           &capture_buffer_.split_bands_f(0)[0], -64)); | 
 |     } | 
 |   } | 
 |  | 
 |   // Test method for testing that the render data is properly received by the | 
 |   // block processor. | 
 |   void RunRenderTransportVerificationTest() { | 
 |     EchoCanceller3 aec3( | 
 |         sample_rate_hz_, false, | 
 |         std::unique_ptr<BlockProcessor>( | 
 |             new RenderTransportVerificationProcessor(num_bands_))); | 
 |  | 
 |     for (size_t frame_index = 0; frame_index < kNumFramesToProcess; | 
 |          ++frame_index) { | 
 |       aec3.AnalyzeCapture(&capture_buffer_); | 
 |       OptionalBandSplit(); | 
 |       PopulateInputFrame(frame_length_, num_bands_, frame_index, | 
 |                          &capture_buffer_.split_bands_f(0)[0], 100); | 
 |       PopulateInputFrame(frame_length_, frame_index, | 
 |                          &render_buffer_.channels_f()[0][0], 0); | 
 |  | 
 |       EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer_)); | 
 |       aec3.ProcessCapture(&capture_buffer_, false); | 
 |       EXPECT_TRUE(VerifyOutputFrameBitexactness( | 
 |           frame_length_, frame_index, &capture_buffer_.split_bands_f(0)[0], | 
 |           -64)); | 
 |     } | 
 |   } | 
 |  | 
 |   // Verifies that information about echo path changes are properly propagated | 
 |   // to the block processor. | 
 |   // The cases tested are: | 
 |   // -That no set echo path change flags are received when there is no echo path | 
 |   // change. | 
 |   // -That set echo path change flags are received and continues to be received | 
 |   // as long as echo path changes are flagged. | 
 |   // -That set echo path change flags are no longer received when echo path | 
 |   // change events stop being flagged. | 
 |   enum class EchoPathChangeTestVariant { kNone, kOneSticky, kOneNonSticky }; | 
 |  | 
 |   void RunEchoPathChangeVerificationTest( | 
 |       EchoPathChangeTestVariant echo_path_change_test_variant) { | 
 |     const size_t num_full_blocks_per_frame = | 
 |         rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100) / kBlockSize; | 
 |     const size_t expected_num_block_to_process = | 
 |         (kNumFramesToProcess * | 
 |          rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)) / | 
 |         kBlockSize; | 
 |     std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>> | 
 |         block_processor_mock( | 
 |             new StrictMock<webrtc::test::MockBlockProcessor>()); | 
 |     EXPECT_CALL(*block_processor_mock, BufferRender(_)) | 
 |         .Times(expected_num_block_to_process) | 
 |         .WillRepeatedly(Return(true)); | 
 |     EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(_)).Times(0); | 
 |  | 
 |     switch (echo_path_change_test_variant) { | 
 |       case EchoPathChangeTestVariant::kNone: | 
 |         EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _)) | 
 |             .Times(expected_num_block_to_process); | 
 |         break; | 
 |       case EchoPathChangeTestVariant::kOneSticky: | 
 |         EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _)) | 
 |             .Times(expected_num_block_to_process); | 
 |         break; | 
 |       case EchoPathChangeTestVariant::kOneNonSticky: | 
 |         EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _)) | 
 |             .Times(num_full_blocks_per_frame); | 
 |         EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _)) | 
 |             .Times(expected_num_block_to_process - num_full_blocks_per_frame); | 
 |         break; | 
 |     } | 
 |  | 
 |     EchoCanceller3 aec3(sample_rate_hz_, false, | 
 |                         std::move(block_processor_mock)); | 
 |  | 
 |     for (size_t frame_index = 0; frame_index < kNumFramesToProcess; | 
 |          ++frame_index) { | 
 |       bool echo_path_change = false; | 
 |       switch (echo_path_change_test_variant) { | 
 |         case EchoPathChangeTestVariant::kNone: | 
 |           break; | 
 |         case EchoPathChangeTestVariant::kOneSticky: | 
 |           echo_path_change = true; | 
 |           break; | 
 |         case EchoPathChangeTestVariant::kOneNonSticky: | 
 |           if (frame_index == 0) { | 
 |             echo_path_change = true; | 
 |           } | 
 |           break; | 
 |       } | 
 |  | 
 |       aec3.AnalyzeCapture(&capture_buffer_); | 
 |       OptionalBandSplit(); | 
 |  | 
 |       PopulateInputFrame(frame_length_, num_bands_, frame_index, | 
 |                          &capture_buffer_.split_bands_f(0)[0], 0); | 
 |       PopulateInputFrame(frame_length_, frame_index, | 
 |                          &render_buffer_.channels_f()[0][0], 0); | 
 |  | 
 |       EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer_)); | 
 |       aec3.ProcessCapture(&capture_buffer_, echo_path_change); | 
 |     } | 
 |   } | 
 |  | 
 |   // Test for verifying that echo leakage information is being properly passed | 
 |   // to the processor. | 
 |   // The cases tested are: | 
 |   // -That no method calls are received when they should not. | 
 |   // -That false values are received each time they are flagged. | 
 |   // -That true values are received each time they are flagged. | 
 |   // -That a false value is received when flagged after a true value has been | 
 |   // flagged. | 
 |   enum class EchoLeakageTestVariant { | 
 |     kNone, | 
 |     kFalseSticky, | 
 |     kTrueSticky, | 
 |     kTrueNonSticky | 
 |   }; | 
 |  | 
 |   void RunEchoLeakageVerificationTest( | 
 |       EchoLeakageTestVariant leakage_report_variant) { | 
 |     const size_t expected_num_block_to_process = | 
 |         (kNumFramesToProcess * | 
 |          rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)) / | 
 |         kBlockSize; | 
 |     std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>> | 
 |         block_processor_mock( | 
 |             new StrictMock<webrtc::test::MockBlockProcessor>()); | 
 |     EXPECT_CALL(*block_processor_mock, BufferRender(_)) | 
 |         .Times(expected_num_block_to_process) | 
 |         .WillRepeatedly(Return(true)); | 
 |     EXPECT_CALL(*block_processor_mock, ProcessCapture(_, _, _)) | 
 |         .Times(expected_num_block_to_process); | 
 |  | 
 |     switch (leakage_report_variant) { | 
 |       case EchoLeakageTestVariant::kNone: | 
 |         EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(_)).Times(0); | 
 |         break; | 
 |       case EchoLeakageTestVariant::kFalseSticky: | 
 |         EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(false)) | 
 |             .Times(1); | 
 |         break; | 
 |       case EchoLeakageTestVariant::kTrueSticky: | 
 |         EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(true)) | 
 |             .Times(1); | 
 |         break; | 
 |       case EchoLeakageTestVariant::kTrueNonSticky: { | 
 |         testing::InSequence s; | 
 |         EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(true)) | 
 |             .Times(1); | 
 |         EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(false)) | 
 |             .Times(kNumFramesToProcess - 1); | 
 |       } break; | 
 |     } | 
 |  | 
 |     EchoCanceller3 aec3(sample_rate_hz_, false, | 
 |                         std::move(block_processor_mock)); | 
 |  | 
 |     for (size_t frame_index = 0; frame_index < kNumFramesToProcess; | 
 |          ++frame_index) { | 
 |       switch (leakage_report_variant) { | 
 |         case EchoLeakageTestVariant::kNone: | 
 |           break; | 
 |         case EchoLeakageTestVariant::kFalseSticky: | 
 |           if (frame_index == 0) { | 
 |             aec3.UpdateEchoLeakageStatus(false); | 
 |           } | 
 |           break; | 
 |         case EchoLeakageTestVariant::kTrueSticky: | 
 |           if (frame_index == 0) { | 
 |             aec3.UpdateEchoLeakageStatus(true); | 
 |           } | 
 |           break; | 
 |         case EchoLeakageTestVariant::kTrueNonSticky: | 
 |           if (frame_index == 0) { | 
 |             aec3.UpdateEchoLeakageStatus(true); | 
 |           } else { | 
 |             aec3.UpdateEchoLeakageStatus(false); | 
 |           } | 
 |           break; | 
 |       } | 
 |  | 
 |       aec3.AnalyzeCapture(&capture_buffer_); | 
 |       OptionalBandSplit(); | 
 |  | 
 |       PopulateInputFrame(frame_length_, num_bands_, frame_index, | 
 |                          &capture_buffer_.split_bands_f(0)[0], 0); | 
 |       PopulateInputFrame(frame_length_, frame_index, | 
 |                          &render_buffer_.channels_f()[0][0], 0); | 
 |  | 
 |       EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer_)); | 
 |       aec3.ProcessCapture(&capture_buffer_, false); | 
 |     } | 
 |   } | 
 |  | 
 |   // This verifies that saturation information is properly passed to the | 
 |   // BlockProcessor. | 
 |   // The cases tested are: | 
 |   // -That no saturation event is passed to the processor if there is no | 
 |   // saturation. | 
 |   // -That one frame with one negative saturated sample value is reported to be | 
 |   // saturated and that following non-saturated frames are properly reported as | 
 |   // not being saturated. | 
 |   // -That one frame with one positive saturated sample value is reported to be | 
 |   // saturated and that following non-saturated frames are properly reported as | 
 |   // not being saturated. | 
 |   enum class SaturationTestVariant { kNone, kOneNegative, kOnePositive }; | 
 |  | 
 |   void RunCaptureSaturationVerificationTest( | 
 |       SaturationTestVariant saturation_variant) { | 
 |     const size_t num_full_blocks_per_frame = | 
 |         rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100) / kBlockSize; | 
 |     const size_t expected_num_block_to_process = | 
 |         (kNumFramesToProcess * | 
 |          rtc::CheckedDivExact(LowestBandRate(sample_rate_hz_), 100)) / | 
 |         kBlockSize; | 
 |     std::unique_ptr<testing::StrictMock<webrtc::test::MockBlockProcessor>> | 
 |         block_processor_mock( | 
 |             new StrictMock<webrtc::test::MockBlockProcessor>()); | 
 |     EXPECT_CALL(*block_processor_mock, BufferRender(_)) | 
 |         .Times(expected_num_block_to_process) | 
 |         .WillRepeatedly(Return(true)); | 
 |     EXPECT_CALL(*block_processor_mock, UpdateEchoLeakageStatus(_)).Times(0); | 
 |  | 
 |     switch (saturation_variant) { | 
 |       case SaturationTestVariant::kNone: | 
 |         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _)) | 
 |             .Times(expected_num_block_to_process); | 
 |         break; | 
 |       case SaturationTestVariant::kOneNegative: { | 
 |         testing::InSequence s; | 
 |         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _)) | 
 |             .Times(num_full_blocks_per_frame); | 
 |         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _)) | 
 |             .Times(expected_num_block_to_process - num_full_blocks_per_frame); | 
 |       } break; | 
 |       case SaturationTestVariant::kOnePositive: { | 
 |         testing::InSequence s; | 
 |         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _)) | 
 |             .Times(num_full_blocks_per_frame); | 
 |         EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _)) | 
 |             .Times(expected_num_block_to_process - num_full_blocks_per_frame); | 
 |       } break; | 
 |     } | 
 |  | 
 |     EchoCanceller3 aec3(sample_rate_hz_, false, | 
 |                         std::move(block_processor_mock)); | 
 |     for (size_t frame_index = 0; frame_index < kNumFramesToProcess; | 
 |          ++frame_index) { | 
 |       for (int k = 0; k < fullband_frame_length_; ++k) { | 
 |         capture_buffer_.channels_f()[0][k] = 0.f; | 
 |       } | 
 |       switch (saturation_variant) { | 
 |         case SaturationTestVariant::kNone: | 
 |           break; | 
 |         case SaturationTestVariant::kOneNegative: | 
 |           if (frame_index == 0) { | 
 |             capture_buffer_.channels_f()[0][10] = -32768.f; | 
 |           } | 
 |           break; | 
 |         case SaturationTestVariant::kOnePositive: | 
 |           if (frame_index == 0) { | 
 |             capture_buffer_.channels_f()[0][10] = 32767.f; | 
 |           } | 
 |           break; | 
 |       } | 
 |  | 
 |       aec3.AnalyzeCapture(&capture_buffer_); | 
 |       OptionalBandSplit(); | 
 |  | 
 |       PopulateInputFrame(frame_length_, num_bands_, frame_index, | 
 |                          &capture_buffer_.split_bands_f(0)[0], 0); | 
 |       PopulateInputFrame(frame_length_, frame_index, | 
 |                          &render_buffer_.channels_f()[0][0], 0); | 
 |  | 
 |       EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer_)); | 
 |       aec3.ProcessCapture(&capture_buffer_, false); | 
 |     } | 
 |   } | 
 |  | 
 |   // This test verifies that the swapqueue is able to handle jitter in the | 
 |   // capture and render API calls. | 
 |   void RunRenderSwapQueueVerificationTest() { | 
 |     EchoCanceller3 aec3( | 
 |         sample_rate_hz_, false, | 
 |         std::unique_ptr<BlockProcessor>( | 
 |             new RenderTransportVerificationProcessor(num_bands_))); | 
 |  | 
 |     constexpr size_t kSwapQueueLength = 30; | 
 |     for (size_t frame_index = 0; frame_index < kSwapQueueLength; | 
 |          ++frame_index) { | 
 |       if (sample_rate_hz_ > 16000) { | 
 |         render_buffer_.SplitIntoFrequencyBands(); | 
 |       } | 
 |       PopulateInputFrame(frame_length_, frame_index, | 
 |                          &render_buffer_.channels_f()[0][0], 0); | 
 |  | 
 |       EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer_)); | 
 |     } | 
 |  | 
 |     for (size_t frame_index = 0; frame_index < kSwapQueueLength; | 
 |          ++frame_index) { | 
 |       aec3.AnalyzeCapture(&capture_buffer_); | 
 |       if (sample_rate_hz_ > 16000) { | 
 |         capture_buffer_.SplitIntoFrequencyBands(); | 
 |       } | 
 |  | 
 |       PopulateInputFrame(frame_length_, num_bands_, frame_index, | 
 |                          &capture_buffer_.split_bands_f(0)[0], 0); | 
 |  | 
 |       aec3.ProcessCapture(&capture_buffer_, false); | 
 |       EXPECT_TRUE(VerifyOutputFrameBitexactness( | 
 |           frame_length_, frame_index, &capture_buffer_.split_bands_f(0)[0], | 
 |           -64)); | 
 |     } | 
 |   } | 
 |  | 
 |   // This test verifies that a buffer overrun in the render swapqueue is | 
 |   // properly reported. | 
 |   void RunRenderPipelineSwapQueueOverrunReturnValueTest() { | 
 |     EchoCanceller3 aec3(sample_rate_hz_, false); | 
 |  | 
 |     constexpr size_t kSwapQueueLength = 30; | 
 |     for (size_t k = 0; k < 2; ++k) { | 
 |       for (size_t frame_index = 0; frame_index < kSwapQueueLength; | 
 |            ++frame_index) { | 
 |         if (sample_rate_hz_ > 16000) { | 
 |           render_buffer_.SplitIntoFrequencyBands(); | 
 |         } | 
 |         PopulateInputFrame(frame_length_, frame_index, | 
 |                            &render_buffer_.channels_f()[0][0], 0); | 
 |  | 
 |         if (k == 0) { | 
 |           EXPECT_TRUE(aec3.AnalyzeRender(&render_buffer_)); | 
 |         } else { | 
 |           EXPECT_FALSE(aec3.AnalyzeRender(&render_buffer_)); | 
 |         } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 | #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) | 
 |   // Verifies the that the check for the number of bands in the AnalyzeRender | 
 |   // input is correct by adjusting the sample rates of EchoCanceller3 and the | 
 |   // input AudioBuffer to have a different number of bands. | 
 |   void RunAnalyzeRenderNumBandsCheckVerification() { | 
 |     // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a | 
 |     // way that the number of bands for the rates are different. | 
 |     const int aec3_sample_rate_hz = sample_rate_hz_ == 48000 ? 32000 : 48000; | 
 |     EchoCanceller3 aec3(aec3_sample_rate_hz, false); | 
 |     PopulateInputFrame(frame_length_, 0, &render_buffer_.channels_f()[0][0], 0); | 
 |  | 
 |     EXPECT_DEATH(aec3.AnalyzeRender(&render_buffer_), ""); | 
 |   } | 
 |  | 
 |   // Verifies the that the check for the number of bands in the ProcessCapture | 
 |   // input is correct by adjusting the sample rates of EchoCanceller3 and the | 
 |   // input AudioBuffer to have a different number of bands. | 
 |   void RunProcessCaptureNumBandsCheckVerification() { | 
 |     // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a | 
 |     // way that the number of bands for the rates are different. | 
 |     const int aec3_sample_rate_hz = sample_rate_hz_ == 48000 ? 32000 : 48000; | 
 |     EchoCanceller3 aec3(aec3_sample_rate_hz, false); | 
 |     PopulateInputFrame(frame_length_, num_bands_, 0, | 
 |                        &capture_buffer_.split_bands_f(0)[0], 100); | 
 |     EXPECT_DEATH(aec3.ProcessCapture(&capture_buffer_, false), ""); | 
 |   } | 
 |  | 
 |   // Verifies the that the check for the frame length in the AnalyzeRender input | 
 |   // is correct by adjusting the sample rates of EchoCanceller3 and the input | 
 |   // AudioBuffer to have a different frame lengths. | 
 |   void RunAnalyzeRenderFrameLengthCheckVerification() { | 
 |     // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a | 
 |     // way that the band frame lengths are different. | 
 |     const int aec3_sample_rate_hz = sample_rate_hz_ == 8000 ? 16000 : 8000; | 
 |     EchoCanceller3 aec3(aec3_sample_rate_hz, false); | 
 |  | 
 |     OptionalBandSplit(); | 
 |     PopulateInputFrame(frame_length_, 0, &render_buffer_.channels_f()[0][0], 0); | 
 |  | 
 |     EXPECT_DEATH(aec3.AnalyzeRender(&render_buffer_), ""); | 
 |   } | 
 |  | 
 |   // Verifies the that the check for the frame length in the AnalyzeRender input | 
 |   // is correct by adjusting the sample rates of EchoCanceller3 and the input | 
 |   // AudioBuffer to have a different frame lengths. | 
 |   void RunProcessCaptureFrameLengthCheckVerification() { | 
 |     // Set aec3_sample_rate_hz to be different from sample_rate_hz_ in such a | 
 |     // way that the band frame lengths are different. | 
 |     const int aec3_sample_rate_hz = sample_rate_hz_ == 8000 ? 16000 : 8000; | 
 |     EchoCanceller3 aec3(aec3_sample_rate_hz, false); | 
 |  | 
 |     OptionalBandSplit(); | 
 |     PopulateInputFrame(frame_length_, num_bands_, 0, | 
 |                        &capture_buffer_.split_bands_f(0)[0], 100); | 
 |  | 
 |     EXPECT_DEATH(aec3.ProcessCapture(&capture_buffer_, false), ""); | 
 |   } | 
 |  | 
 | #endif | 
 |  | 
 |  private: | 
 |   void OptionalBandSplit() { | 
 |     if (sample_rate_hz_ > 16000) { | 
 |       capture_buffer_.SplitIntoFrequencyBands(); | 
 |       render_buffer_.SplitIntoFrequencyBands(); | 
 |     } | 
 |   } | 
 |  | 
 |   static constexpr size_t kNumFramesToProcess = 20; | 
 |   const int sample_rate_hz_; | 
 |   const size_t num_bands_; | 
 |   const size_t frame_length_; | 
 |   const int fullband_frame_length_; | 
 |   AudioBuffer capture_buffer_; | 
 |   AudioBuffer render_buffer_; | 
 |  | 
 |   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(EchoCanceller3Tester); | 
 | }; | 
 |  | 
 | std::string ProduceDebugText(int sample_rate_hz) { | 
 |   std::ostringstream ss; | 
 |   ss << "Sample rate: " << sample_rate_hz; | 
 |   return ss.str(); | 
 | } | 
 |  | 
 | std::string ProduceDebugText(int sample_rate_hz, int variant) { | 
 |   std::ostringstream ss; | 
 |   ss << "Sample rate: " << sample_rate_hz << ", variant: " << variant; | 
 |   return ss.str(); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | TEST(EchoCanceller3Buffering, CaptureBitexactness) { | 
 |   for (auto rate : {8000, 16000, 32000, 48000}) { | 
 |     SCOPED_TRACE(ProduceDebugText(rate)); | 
 |     EchoCanceller3Tester(rate).RunCaptureTransportVerificationTest(); | 
 |   } | 
 | } | 
 |  | 
 | TEST(EchoCanceller3Buffering, RenderBitexactness) { | 
 |   for (auto rate : {8000, 16000, 32000, 48000}) { | 
 |     SCOPED_TRACE(ProduceDebugText(rate)); | 
 |     EchoCanceller3Tester(rate).RunRenderTransportVerificationTest(); | 
 |   } | 
 | } | 
 |  | 
 | TEST(EchoCanceller3Buffering, RenderSwapQueue) { | 
 |   for (auto rate : {8000, 16000, 32000, 48000}) { | 
 |     SCOPED_TRACE(ProduceDebugText(rate)); | 
 |     EchoCanceller3Tester(rate).RunRenderSwapQueueVerificationTest(); | 
 |   } | 
 | } | 
 |  | 
 | TEST(EchoCanceller3Buffering, RenderSwapQueueOverrunReturnValue) { | 
 |   for (auto rate : {8000, 16000, 32000, 48000}) { | 
 |     SCOPED_TRACE(ProduceDebugText(rate)); | 
 |     EchoCanceller3Tester(rate) | 
 |         .RunRenderPipelineSwapQueueOverrunReturnValueTest(); | 
 |   } | 
 | } | 
 |  | 
 | TEST(EchoCanceller3Messaging, CaptureSaturation) { | 
 |   auto variants = {EchoCanceller3Tester::SaturationTestVariant::kNone, | 
 |                    EchoCanceller3Tester::SaturationTestVariant::kOneNegative, | 
 |                    EchoCanceller3Tester::SaturationTestVariant::kOnePositive}; | 
 |   for (auto rate : {8000, 16000, 32000, 48000}) { | 
 |     for (auto variant : variants) { | 
 |       SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant))); | 
 |       EchoCanceller3Tester(rate).RunCaptureSaturationVerificationTest(variant); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | TEST(EchoCanceller3Messaging, EchoPathChange) { | 
 |   auto variants = { | 
 |       EchoCanceller3Tester::EchoPathChangeTestVariant::kNone, | 
 |       EchoCanceller3Tester::EchoPathChangeTestVariant::kOneSticky, | 
 |       EchoCanceller3Tester::EchoPathChangeTestVariant::kOneNonSticky}; | 
 |   for (auto rate : {8000, 16000, 32000, 48000}) { | 
 |     for (auto variant : variants) { | 
 |       SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant))); | 
 |       EchoCanceller3Tester(rate).RunEchoPathChangeVerificationTest(variant); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | TEST(EchoCanceller3Messaging, EchoLeakage) { | 
 |   auto variants = { | 
 |       EchoCanceller3Tester::EchoLeakageTestVariant::kNone, | 
 |       EchoCanceller3Tester::EchoLeakageTestVariant::kFalseSticky, | 
 |       EchoCanceller3Tester::EchoLeakageTestVariant::kTrueSticky, | 
 |       EchoCanceller3Tester::EchoLeakageTestVariant::kTrueNonSticky}; | 
 |   for (auto rate : {8000, 16000, 32000, 48000}) { | 
 |     for (auto variant : variants) { | 
 |       SCOPED_TRACE(ProduceDebugText(rate, static_cast<int>(variant))); | 
 |       EchoCanceller3Tester(rate).RunEchoLeakageVerificationTest(variant); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | #if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID) | 
 |  | 
 | TEST(EchoCanceller3InputCheck, WrongCaptureNumBandsCheckVerification) { | 
 |   for (auto rate : {8000, 16000, 32000, 48000}) { | 
 |     SCOPED_TRACE(ProduceDebugText(rate)); | 
 |     EchoCanceller3Tester(rate).RunProcessCaptureNumBandsCheckVerification(); | 
 |   } | 
 | } | 
 |  | 
 | // TODO(peah): Re-enable the test once the issue with memory leaks during DEATH | 
 | // tests on test bots has been fixed. | 
 | TEST(EchoCanceller3InputCheck, | 
 |      DISABLED_WrongRenderFrameLengthCheckVerification) { | 
 |   for (auto rate : {8000, 16000}) { | 
 |     SCOPED_TRACE(ProduceDebugText(rate)); | 
 |     EchoCanceller3Tester(rate).RunAnalyzeRenderFrameLengthCheckVerification(); | 
 |   } | 
 | } | 
 |  | 
 | TEST(EchoCanceller3InputCheck, WrongCaptureFrameLengthCheckVerification) { | 
 |   for (auto rate : {8000, 16000}) { | 
 |     SCOPED_TRACE(ProduceDebugText(rate)); | 
 |     EchoCanceller3Tester(rate).RunProcessCaptureFrameLengthCheckVerification(); | 
 |   } | 
 | } | 
 |  | 
 | // Verifiers that the verification for null input to the render analysis api | 
 | // call works. | 
 | TEST(EchoCanceller3InputCheck, NullRenderAnalysisParameter) { | 
 |   EXPECT_DEATH(EchoCanceller3(8000, false).AnalyzeRender(nullptr), ""); | 
 | } | 
 |  | 
 | // Verifiers that the verification for null input to the capture analysis api | 
 | // call works. | 
 | TEST(EchoCanceller3InputCheck, NullCaptureAnalysisParameter) { | 
 |   EXPECT_DEATH(EchoCanceller3(8000, false).AnalyzeCapture(nullptr), ""); | 
 | } | 
 |  | 
 | // Verifiers that the verification for null input to the capture processing api | 
 | // call works. | 
 | TEST(EchoCanceller3InputCheck, NullCaptureProcessingParameter) { | 
 |   EXPECT_DEATH(EchoCanceller3(8000, false).ProcessCapture(nullptr, false), ""); | 
 | } | 
 |  | 
 | // Verifies the check for correct sample rate. | 
 | TEST(EchoCanceller3InputCheck, WrongSampleRate) { | 
 |   ApmDataDumper data_dumper(0); | 
 |   EXPECT_DEATH(EchoCanceller3(8001, false), ""); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | }  // namespace webrtc |