diff options
Diffstat (limited to 'src/modules/audio_processing/main/test/unit_test/unit_test.cc')
-rw-r--r-- | src/modules/audio_processing/main/test/unit_test/unit_test.cc | 881 |
1 files changed, 881 insertions, 0 deletions
diff --git a/src/modules/audio_processing/main/test/unit_test/unit_test.cc b/src/modules/audio_processing/main/test/unit_test/unit_test.cc new file mode 100644 index 0000000000..3a6fce5a3f --- /dev/null +++ b/src/modules/audio_processing/main/test/unit_test/unit_test.cc @@ -0,0 +1,881 @@ +/* + * 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 <cstdio> + +#include <gtest/gtest.h> + +#include "audio_processing.h" +#include "audio_processing_unittest.pb.h" +#include "event_wrapper.h" +#include "module_common_types.h" +#include "thread_wrapper.h" +#include "trace.h" +#include "signal_processing_library.h" + +using webrtc::AudioProcessing; +using webrtc::AudioFrame; +using webrtc::GainControl; +using webrtc::NoiseSuppression; +using webrtc::EchoCancellation; +using webrtc::EventWrapper; +using webrtc::Trace; +using webrtc::LevelEstimator; +using webrtc::EchoCancellation; +using webrtc::EchoControlMobile; +using webrtc::VoiceDetection; + +namespace { +// When true, this will compare the output data with the results stored to +// file. This is the typical case. When the file should be updated, it can +// be set to false with the command-line switch --write_output_data. +bool global_read_output_data = true; + +class ApmEnvironment : public ::testing::Environment { + public: + virtual void SetUp() { + Trace::CreateTrace(); + ASSERT_EQ(0, Trace::SetTraceFile("apm_trace.txt")); + } + + virtual void TearDown() { + Trace::ReturnTrace(); + } +}; + +class ApmTest : public ::testing::Test { + protected: + ApmTest(); + virtual void SetUp(); + virtual void TearDown(); + + webrtc::AudioProcessing* apm_; + webrtc::AudioFrame* frame_; + webrtc::AudioFrame* revframe_; + FILE* far_file_; + FILE* near_file_; + bool update_output_data_; +}; + +ApmTest::ApmTest() + : apm_(NULL), + far_file_(NULL), + near_file_(NULL), + frame_(NULL), + revframe_(NULL) {} + +void ApmTest::SetUp() { + apm_ = AudioProcessing::Create(0); + ASSERT_TRUE(apm_ != NULL); + + frame_ = new AudioFrame(); + revframe_ = new AudioFrame(); + + 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)); + + frame_->_payloadDataLengthInSamples = 320; + frame_->_audioChannel = 2; + frame_->_frequencyInHz = 32000; + revframe_->_payloadDataLengthInSamples = 320; + revframe_->_audioChannel = 2; + revframe_->_frequencyInHz = 32000; + + far_file_ = fopen("aec_far.pcm", "rb"); + ASSERT_TRUE(far_file_ != NULL) << "Could not open input file aec_far.pcm\n"; + near_file_ = fopen("aec_near.pcm", "rb"); + ASSERT_TRUE(near_file_ != NULL) << "Could not open input file aec_near.pcm\n"; +} + +void ApmTest::TearDown() { + if (frame_) { + delete frame_; + } + frame_ = NULL; + + if (revframe_) { + delete revframe_; + } + revframe_ = NULL; + + if (far_file_) { + ASSERT_EQ(0, fclose(far_file_)); + } + far_file_ = NULL; + + if (near_file_) { + ASSERT_EQ(0, fclose(near_file_)); + } + near_file_ = NULL; + + if (apm_ != NULL) { + AudioProcessing::Destroy(apm_); + } + apm_ = NULL; +} + +void MixStereoToMono(WebRtc_Word16* stereo, + WebRtc_Word16* mono, + int numSamples) { + for (int i = 0; i < numSamples; i++) { + int int32 = (static_cast<int>(stereo[i * 2]) + + static_cast<int>(stereo[i * 2 + 1])) >> 1; + mono[i] = static_cast<WebRtc_Word16>(int32); + } +} + +void WriteMessageLiteToFile(const char* filename, + const ::google::protobuf::MessageLite& message) { + assert(filename != NULL); + + FILE* file = fopen(filename, "wb"); + ASSERT_TRUE(file != NULL) << "Could not open " << filename; + int size = message.ByteSize(); + ASSERT_GT(size, 0); + unsigned char* array = new unsigned char[size]; + ASSERT_TRUE(message.SerializeToArray(array, size)); + + ASSERT_EQ(1, fwrite(&size, sizeof(int), 1, file)); + ASSERT_EQ(size, fwrite(array, sizeof(unsigned char), size, file)); + + delete [] array; + fclose(file); +} + +void ReadMessageLiteFromFile(const char* filename, + ::google::protobuf::MessageLite* message) { + assert(filename != NULL); + assert(message != NULL); + + FILE* file = fopen(filename, "rb"); + ASSERT_TRUE(file != NULL) << "Could not open " << filename; + int size = 0; + ASSERT_EQ(1, fread(&size, sizeof(int), 1, file)); + ASSERT_GT(size, 0); + unsigned char* array = new unsigned char[size]; + ASSERT_EQ(size, fread(array, sizeof(unsigned char), size, file)); + + ASSERT_TRUE(message->ParseFromArray(array, size)); + + delete [] array; + fclose(file); +} + +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<ThreadData*>(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; +} + +/*TEST_F(ApmTest, Deadlock) { + const int num_threads = 16; + std::vector<ThreadWrapper*> threads(num_threads); + std::vector<ThreadData*> 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, StreamParameters) { + // No errors when the components are disabled. + EXPECT_EQ(apm_->kNoError, + apm_->ProcessStream(frame_)); + + // Missing agc level + EXPECT_EQ(apm_->kNoError, apm_->Initialize()); + EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + apm_->ProcessStream(frame_)); + 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(frame_)); + EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); + + // Missing delay + EXPECT_EQ(apm_->kNoError, apm_->Initialize()); + EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); + 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(frame_)); + EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(false)); + + // Missing drift + EXPECT_EQ(apm_->kNoError, apm_->Initialize()); + EXPECT_EQ(apm_->kNoError, + apm_->echo_cancellation()->enable_drift_compensation(true)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + apm_->ProcessStream(frame_)); + EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); + 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(frame_)); + + // No stream parameters + EXPECT_EQ(apm_->kNoError, apm_->Initialize()); + EXPECT_EQ(apm_->kNoError, + apm_->AnalyzeReverseStream(revframe_)); + EXPECT_EQ(apm_->kStreamParameterNotSetError, + apm_->ProcessStream(frame_)); + + // All there + EXPECT_EQ(apm_->kNoError, apm_->gain_control()->Enable(true)); + 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(frame_)); +} + +TEST_F(ApmTest, Channels) { + // Testing number of invalid channels + EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(0, 1)); + EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(1, 0)); + EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(3, 1)); + EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(1, 3)); + EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_reverse_channels(0)); + EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_reverse_channels(3)); + // Testing number of valid channels + for (int i = 1; i < 3; i++) { + for (int j = 1; j < 3; j++) { + if (j > i) { + EXPECT_EQ(apm_->kBadParameterError, apm_->set_num_channels(i, j)); + } else { + EXPECT_EQ(apm_->kNoError, apm_->set_num_channels(i, j)); + EXPECT_EQ(j, apm_->num_output_channels()); + } + } + EXPECT_EQ(i, apm_->num_input_channels()); + EXPECT_EQ(apm_->kNoError, apm_->set_num_reverse_channels(i)); + EXPECT_EQ(i, apm_->num_reverse_channels()); + } +} + +TEST_F(ApmTest, SampleRates) { + // Testing invalid sample rates + EXPECT_EQ(apm_->kBadParameterError, apm_->set_sample_rate_hz(10000)); + // Testing valid sample rates + int fs[] = {8000, 16000, 32000}; + for (size_t i = 0; i < sizeof(fs) / sizeof(*fs); i++) { + EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(fs[i])); + EXPECT_EQ(fs[i], apm_->sample_rate_hz()); + } +} + +TEST_F(ApmTest, Process) { + GOOGLE_PROTOBUF_VERIFY_VERSION; + audio_processing_unittest::OutputData output_data; + + if (global_read_output_data) { + ReadMessageLiteFromFile("output_data.pb", &output_data); + + } else { + // We don't have a file; add the required tests to the protobuf. + int rev_ch[] = {1, 2}; + int ch[] = {1, 2}; + int fs[] = {8000, 16000, 32000}; + for (size_t i = 0; i < sizeof(rev_ch) / sizeof(*rev_ch); i++) { + for (size_t j = 0; j < sizeof(ch) / sizeof(*ch); j++) { + for (size_t k = 0; k < sizeof(fs) / sizeof(*fs); k++) { + audio_processing_unittest::Test* test = output_data.add_test(); + test->set_numreversechannels(rev_ch[i]); + test->set_numchannels(ch[j]); + test->set_samplerate(fs[k]); + } + } + } + } + + 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_->kNoError, + // apm_->level_estimator()->Enable(true)); + + EXPECT_EQ(apm_->kNoError, + apm_->noise_suppression()->Enable(true)); + + EXPECT_EQ(apm_->kNoError, + apm_->voice_detection()->Enable(true)); + + for (int i = 0; i < output_data.test_size(); i++) { + printf("Running test %d of %d...\n", i + 1, output_data.test_size()); + + audio_processing_unittest::Test* test = output_data.mutable_test(i); + const int num_samples = test->samplerate() / 100; + revframe_->_payloadDataLengthInSamples = num_samples; + revframe_->_audioChannel = test->numreversechannels(); + revframe_->_frequencyInHz = test->samplerate(); + frame_->_payloadDataLengthInSamples = num_samples; + frame_->_audioChannel = test->numchannels(); + frame_->_frequencyInHz = test->samplerate(); + + EXPECT_EQ(apm_->kNoError, apm_->Initialize()); + ASSERT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(test->samplerate())); + ASSERT_EQ(apm_->kNoError, apm_->set_num_channels(frame_->_audioChannel, + frame_->_audioChannel)); + ASSERT_EQ(apm_->kNoError, + apm_->set_num_reverse_channels(revframe_->_audioChannel)); + + + int has_echo_count = 0; + int has_voice_count = 0; + int is_saturated_count = 0; + + while (1) { + WebRtc_Word16 temp_data[640]; + int analog_level = 127; + + // Read far-end frame + size_t read_count = fread(temp_data, + sizeof(WebRtc_Word16), + num_samples * 2, + far_file_); + if (read_count != static_cast<size_t>(num_samples * 2)) { + // Check that the file really ended. + ASSERT_NE(0, feof(far_file_)); + break; // This is expected. + } + + if (revframe_->_audioChannel == 1) { + MixStereoToMono(temp_data, revframe_->_payloadData, + revframe_->_payloadDataLengthInSamples); + } else { + memcpy(revframe_->_payloadData, + &temp_data[0], + sizeof(WebRtc_Word16) * read_count); + } + + EXPECT_EQ(apm_->kNoError, + apm_->AnalyzeReverseStream(revframe_)); + + EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(0)); + EXPECT_EQ(apm_->kNoError, + apm_->echo_cancellation()->set_stream_drift_samples(0)); + EXPECT_EQ(apm_->kNoError, + apm_->gain_control()->set_stream_analog_level(analog_level)); + + // Read near-end frame + read_count = fread(temp_data, + sizeof(WebRtc_Word16), + num_samples * 2, + near_file_); + if (read_count != static_cast<size_t>(num_samples * 2)) { + // Check that the file really ended. + ASSERT_NE(0, feof(near_file_)); + break; // This is expected. + } + + if (frame_->_audioChannel == 1) { + MixStereoToMono(temp_data, frame_->_payloadData, num_samples); + } else { + memcpy(frame_->_payloadData, + &temp_data[0], + sizeof(WebRtc_Word16) * read_count); + } + + EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_)); + + if (apm_->echo_cancellation()->stream_has_echo()) { + has_echo_count++; + } + + analog_level = apm_->gain_control()->stream_analog_level(); + if (apm_->gain_control()->stream_is_saturated()) { + is_saturated_count++; + } + if (apm_->voice_detection()->stream_has_voice()) { + has_voice_count++; + } + } + + //<-- Statistics --> + //LevelEstimator::Metrics far_metrics; + //LevelEstimator::Metrics near_metrics; + //EchoCancellation::Metrics echo_metrics; + //LevelEstimator::Metrics far_metrics_ref_; + //LevelEstimator::Metrics near_metrics_ref_; + //EchoCancellation::Metrics echo_metrics_ref_; + //EXPECT_EQ(apm_->kNoError, + // apm_->echo_cancellation()->GetMetrics(&echo_metrics)); + //EXPECT_EQ(apm_->kNoError, + // apm_->level_estimator()->GetMetrics(&near_metrics, + + // TODO(ajm): check echo metrics and output audio. + if (global_read_output_data) { + EXPECT_EQ(has_echo_count, + test->hasechocount()); + EXPECT_EQ(has_voice_count, + test->hasvoicecount()); + EXPECT_EQ(is_saturated_count, + test->issaturatedcount()); + } else { + test->set_hasechocount(has_echo_count); + test->set_hasvoicecount(has_voice_count); + test->set_issaturatedcount(is_saturated_count); + } + + rewind(far_file_); + rewind(near_file_); + } + + if (!global_read_output_data) { + WriteMessageLiteToFile("output_data.pb", output_data); + } + + google::protobuf::ShutdownProtobufLibrary(); +} + +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<EchoCancellation::SuppressionLevel>(-1))); + + EXPECT_EQ(apm_->kBadParameterError, + apm_->echo_cancellation()->set_suppression_level( + static_cast<EchoCancellation::SuppressionLevel>(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) { + // AECM won't use super-wideband. + EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(32000)); + EXPECT_EQ(apm_->kBadSampleRateError, apm_->echo_control_mobile()->Enable(true)); + EXPECT_EQ(apm_->kNoError, apm_->set_sample_rate_hz(16000)); + // Turn AECM on (and AEC off) + EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true)); + EXPECT_TRUE(apm_->echo_control_mobile()->is_enabled()); + + EXPECT_EQ(apm_->kBadParameterError, + apm_->echo_control_mobile()->set_routing_mode( + static_cast<EchoControlMobile::RoutingMode>(-1))); + EXPECT_EQ(apm_->kBadParameterError, + apm_->echo_control_mobile()->set_routing_mode( + static_cast<EchoControlMobile::RoutingMode>(5))); + + // 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<GainControl::Mode>(-1))); + + EXPECT_EQ(apm_->kBadParameterError, + apm_->gain_control()->set_mode(static_cast<GainControl::Mode>(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<NoiseSuppression::Level>(-1))); + + EXPECT_EQ(apm_->kBadParameterError, + apm_->noise_suppression()->set_level( + static_cast<NoiseSuppression::Level>(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<VoiceDetection::Likelihood>(-1))); + + EXPECT_EQ(apm_->kBadParameterError, + apm_->voice_detection()->set_likelihood( + static_cast<VoiceDetection::Likelihood>(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) +{ +} +*/ +} // namespace + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ApmEnvironment* env = new ApmEnvironment; // GTest takes ownership. + ::testing::AddGlobalTestEnvironment(env); + + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--write_output_data") == 0) { + global_read_output_data = false; + } + } + + return RUN_ALL_TESTS(); +} |