/* * Copyright (c) 2011 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 "unit_test.h" #include "event_wrapper.h" #include "module_common_types.h" #include "thread_wrapper.h" #include "trace.h" #include "signal_processing_library.h" #include "audio_processing.h" namespace webrtc { namespace { struct ThreadData { ThreadData(int thread_num_, AudioProcessing* ap_) : thread_num(thread_num_), error(false), ap(ap_) {} int thread_num; bool error; AudioProcessing* ap; }; // Don't use GTest here; non-thread-safe on Windows (as of 1.5.0). bool DeadlockProc(void* thread_object) { ThreadData* thread_data = static_cast(thread_object); AudioProcessing* ap = thread_data->ap; int err = ap->kNoError; AudioFrame primary_frame; AudioFrame reverse_frame; primary_frame._payloadDataLengthInSamples = 320; primary_frame._audioChannel = 2; primary_frame._frequencyInHz = 32000; reverse_frame._payloadDataLengthInSamples = 320; reverse_frame._audioChannel = 2; reverse_frame._frequencyInHz = 32000; ap->echo_cancellation()->Enable(true); ap->gain_control()->Enable(true); ap->high_pass_filter()->Enable(true); ap->level_estimator()->Enable(true); ap->noise_suppression()->Enable(true); ap->voice_detection()->Enable(true); if (thread_data->thread_num % 2 == 0) { err = ap->AnalyzeReverseStream(&reverse_frame); if (err != ap->kNoError) { printf("Error in AnalyzeReverseStream(): %d\n", err); thread_data->error = true; return false; } } if (thread_data->thread_num % 2 == 1) { ap->set_stream_delay_ms(0); ap->echo_cancellation()->set_stream_drift_samples(0); ap->gain_control()->set_stream_analog_level(0); err = ap->ProcessStream(&primary_frame); if (err == ap->kStreamParameterNotSetError) { printf("Expected kStreamParameterNotSetError in ProcessStream(): %d\n", err); } else if (err != ap->kNoError) { printf("Error in ProcessStream(): %d\n", err); thread_data->error = true; return false; } ap->gain_control()->stream_analog_level(); } EventWrapper* event = EventWrapper::Create(); event->Wait(1); delete event; event = NULL; return true; } } // namespace class ApmEnvironment : public ::testing::Environment { public: virtual void SetUp() { Trace::CreateTrace(); ASSERT_EQ(0, Trace::SetTraceFile("ApmTrace.txt")); } virtual void TearDown() { Trace::ReturnTrace(); } }; ApmTest::ApmTest() : apm_(NULL), far_file_(NULL), near_file_(NULL), stat_file_(NULL), read_stat_(true) { } void ApmTest::SetUp() { apm_ = AudioProcessing::Create(0); ASSERT_TRUE(apm_ != NULL); far_file_ = fopen("aecFar.pcm", "rb"); ASSERT_TRUE(far_file_ != NULL) << "Cannot read source file aecFar.pcm\n"; near_file_ = fopen("aecNear.pcm", "rb"); ASSERT_TRUE(near_file_ != NULL) << "Cannot read source file aecNear.pcm\n"; if (read_stat_) { stat_file_ = fopen("statData.dat", "rb"); ASSERT_TRUE(stat_file_ != NULL) << "Cannot write to source file statData.dat\n"; } } void ApmTest::TearDown() { if (far_file_ != NULL) { ASSERT_EQ(0, fclose(far_file_)); } far_file_ = NULL; if (near_file_ != NULL) { ASSERT_EQ(0, fclose(near_file_)); } near_file_ = NULL; if (stat_file_ != NULL) { ASSERT_EQ(0, fclose(stat_file_)); } stat_file_ = NULL; if (apm_ != NULL) { AudioProcessing::Destroy(apm_); } apm_ = NULL; } TEST_F(ApmTest, Deadlock) { const int num_threads = 16; std::vector threads(num_threads); std::vector thread_data(num_threads); ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(32000)); ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(2, 2)); ASSERT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(2)); for (int i = 0; i < num_threads; i++) { thread_data[i] = new ThreadData(i, apm_); threads[i] = ThreadWrapper::CreateThread(DeadlockProc, thread_data[i], kNormalPriority, 0); ASSERT_TRUE(threads[i] != NULL); unsigned int thread_id = 0; threads[i]->Start(thread_id); } EventWrapper* event = EventWrapper::Create(); ASSERT_EQ(kEventTimeout, event->Wait(5000)); delete event; event = NULL; for (int i = 0; i < num_threads; i++) { // This will return false if the thread has deadlocked. ASSERT_TRUE(threads[i]->Stop()); ASSERT_FALSE(thread_data[i]->error); delete threads[i]; threads[i] = NULL; delete thread_data[i]; thread_data[i] = NULL; } } TEST_F(ApmTest, Process) { if (!read_stat_) { stat_file_ = fopen("statData.dat", "wb"); ASSERT_TRUE(stat_file_ != NULL) << "Cannot write to source file statData.dat\n"; } // Testing number of invalid channels ASSERT_EQ(apm_->kBadParameterError, apm_->set_num_channels(0, 1)); ASSERT_EQ(apm_->kBadParameterError, apm_->set_num_channels(1, 0)); ASSERT_EQ(apm_->kBadParameterError, apm_->set_num_channels(3, 1)); ASSERT_EQ(apm_->kBadParameterError, apm_->set_num_channels(1, 3)); ASSERT_EQ(apm_->kBadParameterError, apm_->set_num_reverse_channels(0)); ASSERT_EQ(apm_->kBadParameterError, apm_->set_num_reverse_channels(3)); // Testing number of valid channels for (int ii = 1; ii < 3; ++ii) { for (int jj = 1; jj < 3; ++jj) { if (jj > ii ) { ASSERT_EQ(apm_->kBadParameterError, apm_->set_num_channels(ii, jj)); } else { ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(ii, jj)); ASSERT_EQ(jj, apm_->num_output_channels()); } } ASSERT_EQ(ii, apm_->num_input_channels()); ASSERT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(ii)); ASSERT_EQ(ii, apm_->num_reverse_channels()); } // Testing invalid sample rates ASSERT_EQ(apm_->kBadParameterError, apm_->set_sample_rate_hz(10000)); // Testing valid sample rates int fs[] = {8000, 16000, 32000}; for (int ii = 0; ii < sizeof(fs)/sizeof(*fs); ++ii) { ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(fs[ii])); ASSERT_EQ(fs[ii], apm_->sample_rate_hz()); } AudioFrame render_audio; AudioFrame capture_audio; render_audio._payloadDataLengthInSamples = 320; render_audio._audioChannel = 2; render_audio._frequencyInHz = 32000; capture_audio._payloadDataLengthInSamples = 320; capture_audio._audioChannel = 2; capture_audio._frequencyInHz = 32000; EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_drift_compensation(true)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_metrics(true)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_analog_level_limits(0, 255)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(true)); EXPECT_EQ(apm_->kUnsupportedComponentError, apm_->level_estimator()->Enable(true)); EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true)); EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true)); LevelEstimator::Metrics far_metrics; LevelEstimator::Metrics near_metrics; EchoCancellation::Metrics echo_metrics; for (int i = 0; i < 100; i++) { EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(&render_audio)); EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->set_stream_drift_samples(0)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_stream_analog_level(127)); EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(&capture_audio)); apm_->echo_cancellation()->stream_has_echo(); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->GetMetrics(&echo_metrics)); apm_->gain_control()->stream_analog_level(); apm_->gain_control()->stream_is_saturated(); EXPECT_EQ(apm_->kUnsupportedComponentError, apm_->level_estimator()->GetMetrics(&near_metrics, &far_metrics)); apm_->voice_detection()->stream_has_voice(); } // Wrong order tests // No stream parameter EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(&render_audio)); EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(&capture_audio)); // Missing agc level EXPECT_EQ(apm_->kNoError, apm_->Initialize()); EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->set_stream_drift_samples(0)); EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(&capture_audio)); // Missing delay EXPECT_EQ(apm_->kNoError, apm_->Initialize()); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->set_stream_drift_samples(0)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_stream_analog_level(127)); EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(&capture_audio)); // Missing drift EXPECT_EQ(apm_->kNoError, apm_->Initialize()); EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_stream_analog_level(127)); EXPECT_EQ(apm_->kStreamParameterNotSetError, apm_->ProcessStream(&capture_audio)); // All there EXPECT_EQ(apm_->kNoError, apm_->Initialize()); EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(100)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->set_stream_drift_samples(0)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_stream_analog_level(127)); EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(&capture_audio)); // Test with real audio // Loop through all possible combinations // (# reverse channels, # channels, sample rates) int revCh[] = {1, 2}; int nRevCh = 2; int ch[] = {1, 2}; int nCh = 2; int nFs = (int)(sizeof(fs)/sizeof(*fs)); if (read_stat_) { fread(&nRevCh, sizeof(int), 1, stat_file_); fread(revCh, sizeof(int), nRevCh, stat_file_); fread(&nCh, sizeof(int), 1, stat_file_); fread(ch, sizeof(int), nCh, stat_file_); fread(&nFs, sizeof(int), 1, stat_file_); fread(fs, 1, sizeof(fs), stat_file_); } else { fwrite(&nRevCh, sizeof(int), 1, stat_file_); fwrite(revCh, sizeof(int), nRevCh, stat_file_); fwrite(&nCh, sizeof(int), 1, stat_file_); fwrite(ch, sizeof(int), nCh, stat_file_); fwrite(&nFs, sizeof(int), 1, stat_file_); fwrite(fs, 1, sizeof(fs), stat_file_); } int testCnt = 0; for (int iRevCh = 0; iRevCh < nRevCh; ++iRevCh) { for (int iCh = 0; iCh < nCh; ++iCh) { for (int iFs = 0; iFs < nFs; ++iFs) { render_audio._payloadDataLengthInSamples = fs[iFs]/100; render_audio._audioChannel = revCh[iRevCh]; render_audio._frequencyInHz = fs[iFs]; capture_audio._payloadDataLengthInSamples = fs[iFs]/100; capture_audio._audioChannel = ch[iCh]; capture_audio._frequencyInHz = fs[iFs]; EXPECT_EQ(apm_->kNoError, apm_->Initialize()); ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(fs[iFs])); ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(capture_audio._audioChannel, capture_audio._audioChannel)); ASSERT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(render_audio._audioChannel)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_drift_compensation(false)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_metrics(true)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_mode(GainControl::kAdaptiveAnalog)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_analog_level_limits(0, 255)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(true)); //EXPECT_EQ(apm_->kNoError, // apm_->level_estimator()->Enable(true)); EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true)); EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true)); bool runningFiles = true; int i = 0; int echo_count = 0; int vad_count = 0; int sat_count = 0; int analog_level = 127; int sat_gain = 2; size_t read_count = 0; WebRtc_Word16 tmpData[640]; int tmp_int; int echo_count_ref_ = 0; int vad_count_ref_ = 0; int sat_count_ref_ = 0; //LevelEstimator::Metrics far_metrics_ref_; //LevelEstimator::Metrics near_metrics_ref_; EchoCancellation::Metrics echo_metrics_ref_; while (runningFiles) { i++; // Read far end frame read_count = fread(&tmpData[0], sizeof(WebRtc_Word16), render_audio._payloadDataLengthInSamples * 2, far_file_); if (read_count != render_audio._payloadDataLengthInSamples * 2) { break; // This is expected. } if (render_audio._audioChannel == 1) { for (int jj = 0; jj < render_audio._payloadDataLengthInSamples; ++jj) { tmp_int = (static_cast(tmpData[jj * 2]) + static_cast(tmpData[jj * 2 + 1])) >> 1; render_audio._payloadData[jj] = static_cast(tmp_int); } } else { memcpy(render_audio._payloadData, &tmpData[0], sizeof(WebRtc_Word16) * read_count); } EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(&render_audio)); EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_stream_analog_level(analog_level)); // Read near end frame read_count = fread(&tmpData[0], sizeof(WebRtc_Word16), capture_audio._payloadDataLengthInSamples * 2, near_file_); if (read_count != capture_audio._payloadDataLengthInSamples * 2) { break; // This is expected. } if (capture_audio._audioChannel == 1) { for (int jj = 0; jj < capture_audio._payloadDataLengthInSamples; ++jj) { tmp_int = (static_cast(tmpData[jj * 2]) + static_cast(tmpData[jj * 2 + 1])) >> 1; capture_audio._payloadData[jj] = static_cast(tmp_int); } } else { memcpy(capture_audio._payloadData, &tmpData[0], sizeof(WebRtc_Word16) * read_count); } WebRtc_Word32 tmpF = 0; for (int jj = 0; jj < read_count; ++jj) { tmpF = (WebRtc_Word32)capture_audio._payloadData[jj] * sat_gain; if (tmpF > WEBRTC_SPL_WORD16_MAX) { capture_audio._payloadData[jj] = WEBRTC_SPL_WORD16_MAX; } else if (tmpF < WEBRTC_SPL_WORD16_MIN) { capture_audio._payloadData[jj] = WEBRTC_SPL_WORD16_MIN; } else { capture_audio._payloadData[jj] = (WebRtc_Word16)tmpF; } } EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(&capture_audio)); if (apm_->echo_cancellation()->stream_has_echo()) { echo_count++; } analog_level = apm_->gain_control()->stream_analog_level(); if (apm_->gain_control()->stream_is_saturated()) { sat_count++; sat_gain = 1; } if (apm_->voice_detection()->stream_has_voice()) { vad_count++; } } //<-- Statistics --> EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->GetMetrics(&echo_metrics)); //EXPECT_EQ(apm_->kNoError, // apm_->level_estimator()->GetMetrics(&near_metrics, // TODO(ajm): Perhaps we don't have to check every value? The average // could be sufficient. Or, how about hashing the output? if (read_stat_) { // Read from statData fread(&echo_count_ref_, 1, sizeof(echo_count), stat_file_); EXPECT_EQ(echo_count_ref_, echo_count); fread(&echo_metrics_ref_, 1, sizeof(EchoCancellation::Metrics), stat_file_); EXPECT_EQ(echo_metrics_ref_.residual_echo_return_loss.instant, echo_metrics.residual_echo_return_loss.instant); EXPECT_EQ(echo_metrics_ref_.residual_echo_return_loss.average, echo_metrics.residual_echo_return_loss.average); EXPECT_EQ(echo_metrics_ref_.residual_echo_return_loss.maximum, echo_metrics.residual_echo_return_loss.maximum); EXPECT_EQ(echo_metrics_ref_.residual_echo_return_loss.minimum, echo_metrics.residual_echo_return_loss.minimum); EXPECT_EQ(echo_metrics_ref_.echo_return_loss.instant, echo_metrics.echo_return_loss.instant); EXPECT_EQ(echo_metrics_ref_.echo_return_loss.average, echo_metrics.echo_return_loss.average); EXPECT_EQ(echo_metrics_ref_.echo_return_loss.maximum, echo_metrics.echo_return_loss.maximum); EXPECT_EQ(echo_metrics_ref_.echo_return_loss.minimum, echo_metrics.echo_return_loss.minimum); EXPECT_EQ(echo_metrics_ref_.echo_return_loss_enhancement.instant, echo_metrics.echo_return_loss_enhancement.instant); EXPECT_EQ(echo_metrics_ref_.echo_return_loss_enhancement.average, echo_metrics.echo_return_loss_enhancement.average); EXPECT_EQ(echo_metrics_ref_.echo_return_loss_enhancement.maximum, echo_metrics.echo_return_loss_enhancement.maximum); EXPECT_EQ(echo_metrics_ref_.echo_return_loss_enhancement.minimum, echo_metrics.echo_return_loss_enhancement.minimum); EXPECT_EQ(echo_metrics_ref_.a_nlp.instant, echo_metrics.a_nlp.instant); EXPECT_EQ(echo_metrics_ref_.a_nlp.average, echo_metrics.a_nlp.average); EXPECT_EQ(echo_metrics_ref_.a_nlp.maximum, echo_metrics.a_nlp.maximum); EXPECT_EQ(echo_metrics_ref_.a_nlp.minimum, echo_metrics.a_nlp.minimum); fread(&vad_count_ref_, 1, sizeof(vad_count), stat_file_); EXPECT_EQ(vad_count_ref_, vad_count); fread(&sat_count_ref_, 1, sizeof(sat_count), stat_file_); EXPECT_EQ(sat_count_ref_, sat_count); /*fread(&far_metrics_ref_, 1, sizeof(LevelEstimator::Metrics), stat_file_); EXPECT_EQ(far_metrics_ref_.signal.instant, far_metrics.signal.instant); EXPECT_EQ(far_metrics_ref_.signal.average, far_metrics.signal.average); EXPECT_EQ(far_metrics_ref_.signal.maximum, far_metrics.signal.maximum); EXPECT_EQ(far_metrics_ref_.signal.minimum, far_metrics.signal.minimum); EXPECT_EQ(far_metrics_ref_.speech.instant, far_metrics.speech.instant); EXPECT_EQ(far_metrics_ref_.speech.average, far_metrics.speech.average); EXPECT_EQ(far_metrics_ref_.speech.maximum, far_metrics.speech.maximum); EXPECT_EQ(far_metrics_ref_.speech.minimum, far_metrics.speech.minimum); EXPECT_EQ(far_metrics_ref_.noise.instant, far_metrics.noise.instant); EXPECT_EQ(far_metrics_ref_.noise.average, far_metrics.noise.average); EXPECT_EQ(far_metrics_ref_.noise.maximum, far_metrics.noise.maximum); EXPECT_EQ(far_metrics_ref_.noise.minimum, far_metrics.noise.minimum); fread(&near_metrics_ref_, 1, sizeof(LevelEstimator::Metrics), stat_file_); EXPECT_EQ(near_metrics_ref_.signal.instant, near_metrics.signal.instant); EXPECT_EQ(near_metrics_ref_.signal.average, near_metrics.signal.average); EXPECT_EQ(near_metrics_ref_.signal.maximum, near_metrics.signal.maximum); EXPECT_EQ(near_metrics_ref_.signal.minimum, near_metrics.signal.minimum); EXPECT_EQ(near_metrics_ref_.speech.instant, near_metrics.speech.instant); EXPECT_EQ(near_metrics_ref_.speech.average, near_metrics.speech.average); EXPECT_EQ(near_metrics_ref_.speech.maximum, near_metrics.speech.maximum); EXPECT_EQ(near_metrics_ref_.speech.minimum, near_metrics.speech.minimum); EXPECT_EQ(near_metrics_ref_.noise.instant, near_metrics.noise.instant); EXPECT_EQ(near_metrics_ref_.noise.average, near_metrics.noise.average); EXPECT_EQ(near_metrics_ref_.noise.maximum, near_metrics.noise.maximum); EXPECT_EQ(near_metrics_ref_.noise.minimum, near_metrics.noise.minimum);*/ } else { // Write to statData fwrite(&echo_count, 1, sizeof(echo_count), stat_file_); fwrite(&echo_metrics, 1, sizeof(EchoCancellation::Metrics), stat_file_); fwrite(&vad_count, 1, sizeof(vad_count), stat_file_); fwrite(&sat_count, 1, sizeof(sat_count), stat_file_); //fwrite(&far_metrics, 1, sizeof(LevelEstimator::Metrics), stat_file_); //fwrite(&near_metrics, 1, sizeof(LevelEstimator::Metrics), stat_file_); } rewind(far_file_); rewind(near_file_); testCnt++; printf("Loop %d of %d\n", testCnt, nRevCh * nCh * nFs); } } } if (!read_stat_) { if (stat_file_ != NULL) { ASSERT_EQ(0, fclose(stat_file_)); } stat_file_ = NULL; } } TEST_F(ApmTest, EchoCancellation) { EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_drift_compensation(true)); EXPECT_TRUE(apm_->echo_cancellation()->is_drift_compensation_enabled()); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_drift_compensation(false)); EXPECT_FALSE(apm_->echo_cancellation()->is_drift_compensation_enabled()); EXPECT_EQ(apm_->kBadParameterError, apm_->echo_cancellation()->set_device_sample_rate_hz(4000)); EXPECT_EQ(apm_->kBadParameterError, apm_->echo_cancellation()->set_device_sample_rate_hz(100000)); int rate[] = {16000, 44100, 48000}; for (size_t i = 0; i < sizeof(rate)/sizeof(*rate); i++) { EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->set_device_sample_rate_hz(rate[i])); EXPECT_EQ(rate[i], apm_->echo_cancellation()->device_sample_rate_hz()); } EXPECT_EQ(apm_->kBadParameterError, apm_->echo_cancellation()->set_suppression_level( static_cast(-1))); EXPECT_EQ(apm_->kBadParameterError, apm_->echo_cancellation()->set_suppression_level( static_cast(4))); EchoCancellation::SuppressionLevel level[] = { EchoCancellation::kLowSuppression, EchoCancellation::kModerateSuppression, EchoCancellation::kHighSuppression, }; for (size_t i = 0; i < sizeof(level)/sizeof(*level); i++) { EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->set_suppression_level(level[i])); EXPECT_EQ(level[i], apm_->echo_cancellation()->suppression_level()); } EchoCancellation::Metrics metrics; EXPECT_EQ(apm_->kNotEnabledError, apm_->echo_cancellation()->GetMetrics(&metrics)); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_metrics(true)); EXPECT_TRUE(apm_->echo_cancellation()->are_metrics_enabled()); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->enable_metrics(false)); EXPECT_FALSE(apm_->echo_cancellation()->are_metrics_enabled()); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); EXPECT_TRUE(apm_->echo_cancellation()->is_enabled()); EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(false)); EXPECT_FALSE(apm_->echo_cancellation()->is_enabled()); } TEST_F(ApmTest, EchoControlMobile) { // Turn AECM on (and AEC off) EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true)); EXPECT_TRUE(apm_->echo_control_mobile()->is_enabled()); // Toggle routing modes EchoControlMobile::RoutingMode mode[] = { EchoControlMobile::kQuietEarpieceOrHeadset, EchoControlMobile::kEarpiece, EchoControlMobile::kLoudEarpiece, EchoControlMobile::kSpeakerphone, EchoControlMobile::kLoudSpeakerphone, }; for (size_t i = 0; i < sizeof(mode)/sizeof(*mode); i++) { EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->set_routing_mode(mode[i])); EXPECT_EQ(mode[i], apm_->echo_control_mobile()->routing_mode()); } // Turn comfort noise off/on EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->enable_comfort_noise(false)); EXPECT_FALSE(apm_->echo_control_mobile()->is_comfort_noise_enabled()); EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->enable_comfort_noise(true)); EXPECT_TRUE(apm_->echo_control_mobile()->is_comfort_noise_enabled()); // Turn AECM off EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false)); EXPECT_FALSE(apm_->echo_control_mobile()->is_enabled()); } TEST_F(ApmTest, GainControl) { // Testing gain modes EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_mode(static_cast(-1))); EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_mode(static_cast(3))); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_mode( apm_->gain_control()->mode())); GainControl::Mode mode[] = { GainControl::kAdaptiveAnalog, GainControl::kAdaptiveDigital, GainControl::kFixedDigital }; for (size_t i = 0; i < sizeof(mode)/sizeof(*mode); i++) { EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_mode(mode[i])); EXPECT_EQ(mode[i], apm_->gain_control()->mode()); } // Testing invalid target levels EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_target_level_dbfs(-3)); EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_target_level_dbfs(-40)); // Testing valid target levels EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_target_level_dbfs( apm_->gain_control()->target_level_dbfs())); int level_dbfs[] = {0, 6, 31}; for (size_t i = 0; i < sizeof(level_dbfs)/sizeof(*level_dbfs); i++) { EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_target_level_dbfs(level_dbfs[i])); EXPECT_EQ(level_dbfs[i], apm_->gain_control()->target_level_dbfs()); } // Testing invalid compression gains EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_compression_gain_db(-1)); EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_compression_gain_db(100)); // Testing valid compression gains EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_compression_gain_db( apm_->gain_control()->compression_gain_db())); int gain_db[] = {0, 10, 90}; for (size_t i = 0; i < sizeof(gain_db)/sizeof(*gain_db); i++) { EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_compression_gain_db(gain_db[i])); EXPECT_EQ(gain_db[i], apm_->gain_control()->compression_gain_db()); } // Testing limiter off/on EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(false)); EXPECT_FALSE(apm_->gain_control()->is_limiter_enabled()); EXPECT_EQ(apm_->kNoError, apm_->gain_control()->enable_limiter(true)); EXPECT_TRUE(apm_->gain_control()->is_limiter_enabled()); // Testing invalid level limits EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_analog_level_limits(-1, 512)); EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_analog_level_limits(100000, 512)); EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_analog_level_limits(512, -1)); EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_analog_level_limits(512, 100000)); EXPECT_EQ(apm_->kBadParameterError, apm_->gain_control()->set_analog_level_limits(512, 255)); // Testing valid level limits EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_analog_level_limits( apm_->gain_control()->analog_level_minimum(), apm_->gain_control()->analog_level_maximum())); int min_level[] = {0, 255, 1024}; for (size_t i = 0; i < sizeof(min_level)/sizeof(*min_level); i++) { EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_analog_level_limits(min_level[i], 1024)); EXPECT_EQ(min_level[i], apm_->gain_control()->analog_level_minimum()); } int max_level[] = {0, 1024, 65535}; for (size_t i = 0; i < sizeof(min_level)/sizeof(*min_level); i++) { EXPECT_EQ(apm_->kNoError, apm_->gain_control()->set_analog_level_limits(0, max_level[i])); EXPECT_EQ(max_level[i], apm_->gain_control()->analog_level_maximum()); } // TODO(ajm): stream_is_saturated() and stream_analog_level() // Turn AGC off EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); EXPECT_FALSE(apm_->gain_control()->is_enabled()); } TEST_F(ApmTest, NoiseSuppression) { // Tesing invalid suppression levels EXPECT_EQ(apm_->kBadParameterError, apm_->noise_suppression()->set_level( static_cast(-1))); EXPECT_EQ(apm_->kBadParameterError, apm_->noise_suppression()->set_level( static_cast(5))); // Tesing valid suppression levels NoiseSuppression::Level level[] = { NoiseSuppression::kLow, NoiseSuppression::kModerate, NoiseSuppression::kHigh, NoiseSuppression::kVeryHigh }; for (size_t i = 0; i < sizeof(level)/sizeof(*level); i++) { EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->set_level(level[i])); EXPECT_EQ(level[i], apm_->noise_suppression()->level()); } // Turing NS on/off EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(true)); EXPECT_TRUE(apm_->noise_suppression()->is_enabled()); EXPECT_EQ(apm_->kNoError, apm_->noise_suppression()->Enable(false)); EXPECT_FALSE(apm_->noise_suppression()->is_enabled()); } TEST_F(ApmTest, HighPassFilter) { // Turing HP filter on/off EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(true)); EXPECT_TRUE(apm_->high_pass_filter()->is_enabled()); EXPECT_EQ(apm_->kNoError, apm_->high_pass_filter()->Enable(false)); EXPECT_FALSE(apm_->high_pass_filter()->is_enabled()); } TEST_F(ApmTest, LevelEstimator) { // Turing Level estimator on/off EXPECT_EQ(apm_->kUnsupportedComponentError, apm_->level_estimator()->Enable(true)); EXPECT_FALSE(apm_->level_estimator()->is_enabled()); EXPECT_EQ(apm_->kUnsupportedComponentError, apm_->level_estimator()->Enable(false)); EXPECT_FALSE(apm_->level_estimator()->is_enabled()); } TEST_F(ApmTest, VoiceDetection) { // Test external VAD EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->set_stream_has_voice(true)); EXPECT_TRUE(apm_->voice_detection()->stream_has_voice()); EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->set_stream_has_voice(false)); EXPECT_FALSE(apm_->voice_detection()->stream_has_voice()); // Tesing invalid likelihoods EXPECT_EQ(apm_->kBadParameterError, apm_->voice_detection()->set_likelihood( static_cast(-1))); EXPECT_EQ(apm_->kBadParameterError, apm_->voice_detection()->set_likelihood( static_cast(5))); // Tesing valid likelihoods VoiceDetection::Likelihood likelihood[] = { VoiceDetection::kVeryLowLikelihood, VoiceDetection::kLowLikelihood, VoiceDetection::kModerateLikelihood, VoiceDetection::kHighLikelihood }; for (size_t i = 0; i < sizeof(likelihood)/sizeof(*likelihood); i++) { EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->set_likelihood(likelihood[i])); EXPECT_EQ(likelihood[i], apm_->voice_detection()->likelihood()); } /* TODO(bjornv): Enable once VAD supports other frame lengths than 10 ms // Tesing invalid frame sizes EXPECT_EQ(apm_->kBadParameterError, apm_->voice_detection()->set_frame_size_ms(12)); // Tesing valid frame sizes for (int i = 10; i <= 30; i += 10) { EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->set_frame_size_ms(i)); EXPECT_EQ(i, apm_->voice_detection()->frame_size_ms()); } */ // Turing VAD on/off EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(true)); EXPECT_TRUE(apm_->voice_detection()->is_enabled()); EXPECT_EQ(apm_->kNoError, apm_->voice_detection()->Enable(false)); EXPECT_FALSE(apm_->voice_detection()->is_enabled()); // TODO(bjornv): Add tests for streamed voice; stream_has_voice() } // Below are some ideas for tests from VPM. /*TEST_F(VideoProcessingModuleTest, GetVersionTest) { } TEST_F(VideoProcessingModuleTest, HandleNullBuffer) { } TEST_F(VideoProcessingModuleTest, HandleBadSize) { } TEST_F(VideoProcessingModuleTest, IdenticalResultsAfterReset) { } */ int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); ApmEnvironment* env = new ApmEnvironment; ::testing::AddGlobalTestEnvironment(env); return RUN_ALL_TESTS(); } }