Added calls to set and get external echo channels.
Review URL: http://webrtc-codereview.appspot.com/62005
git-svn-id: http://webrtc.googlecode.com/svn/trunk@196 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/src/modules/audio_processing/aecm/main/interface/echo_control_mobile.h b/src/modules/audio_processing/aecm/main/interface/echo_control_mobile.h
index 26b1172..d50da71 100644
--- a/src/modules/audio_processing/aecm/main/interface/echo_control_mobile.h
+++ b/src/modules/audio_processing/aecm/main/interface/echo_control_mobile.h
@@ -74,16 +74,14 @@
* -------------------------------------------------------------------
* void *aecmInst Pointer to the AECM instance
* WebRtc_Word32 sampFreq Sampling frequency of data
- * WebRtc_Word32 scSampFreq Soundcard sampling frequency
*
* Outputs Description
* -------------------------------------------------------------------
- * WebRtc_Word32 return 0: OK
+ * WebRtc_Word32 return 0: OK
* -1: error
*/
WebRtc_Word32 WebRtcAecm_Init(void* aecmInst,
- WebRtc_Word32 sampFreq,
- WebRtc_Word32 scSampFreq);
+ WebRtc_Word32 sampFreq);
/*
* Inserts an 80 or 160 sample block of data into the farend buffer.
@@ -171,6 +169,52 @@
AecmConfig *config);
/*
+ * This function enables the user to set the echo path on-the-fly.
+ *
+ * Inputs Description
+ * -------------------------------------------------------------------
+ * void* aecmInst Pointer to the AECM instance
+ * void* echo_path Pointer to the echo path to be set
+ * int size_bytes Size in bytes of the echo path
+ *
+ * Outputs Description
+ * -------------------------------------------------------------------
+ * WebRtc_Word32 return 0: OK
+ * -1: error
+ */
+WebRtc_Word32 WebRtcAecm_InitEchoPath(void* aecmInst,
+ const void* echo_path,
+ int size_bytes);
+
+/*
+ * This function enables the user to get the currently used echo path
+ * on-the-fly
+ *
+ * Inputs Description
+ * -------------------------------------------------------------------
+ * void* aecmInst Pointer to the AECM instance
+ * void* echo_path Pointer to echo path
+ * int size_bytes Size in bytes of the echo path
+ *
+ * Outputs Description
+ * -------------------------------------------------------------------
+ * WebRtc_Word32 return 0: OK
+ * -1: error
+ */
+WebRtc_Word32 WebRtcAecm_GetEchoPath(void* aecmInst,
+ void* echo_path,
+ int size_bytes);
+
+/*
+ * This function enables the user to get the echo path size in bytes
+ *
+ * Outputs Description
+ * -------------------------------------------------------------------
+ * int return : size in bytes
+ */
+int WebRtcAecm_echo_path_size_bytes();
+
+/*
* Gets the last error code.
*
* Inputs Description
diff --git a/src/modules/audio_processing/aecm/main/source/aecm_core.c b/src/modules/audio_processing/aecm/main/source/aecm_core.c
index f17f1bf..28a3b1c 100644
--- a/src/modules/audio_processing/aecm/main/source/aecm_core.c
+++ b/src/modules/audio_processing/aecm/main/source/aecm_core.c
@@ -253,6 +253,27 @@
return 0;
}
+void WebRtcAecm_InitEchoPathCore(AecmCore_t* aecm, const WebRtc_Word16* echo_path)
+{
+ int i = 0;
+
+ // Reset the stored channel
+ memcpy(aecm->channelStored, echo_path, sizeof(WebRtc_Word16) * PART_LEN1);
+ // Reset the adapted channels
+ memcpy(aecm->channelAdapt16, echo_path, sizeof(WebRtc_Word16) * PART_LEN1);
+ for (i = 0; i < PART_LEN1; i++)
+ {
+ aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32(
+ (WebRtc_Word32)(aecm->channelAdapt16[i]), 16);
+ }
+
+ // Reset channel storing variables
+ aecm->mseAdaptOld = 1000;
+ aecm->mseStoredOld = 1000;
+ aecm->mseThreshold = WEBRTC_SPL_WORD32_MAX;
+ aecm->mseChannelCount = 0;
+}
+
// WebRtcAecm_InitCore(...)
//
// This function initializes the AECM instant created with WebRtcAecm_CreateCore(...)
@@ -329,17 +350,11 @@
// Initialize the echo channels with a stored shape.
if (samplingFreq == 8000)
{
- memcpy(aecm->channelAdapt16, kChannelStored8kHz, sizeof(WebRtc_Word16) * PART_LEN1);
+ WebRtcAecm_InitEchoPathCore(aecm, kChannelStored8kHz);
}
else
{
- memcpy(aecm->channelAdapt16, kChannelStored16kHz, sizeof(WebRtc_Word16) * PART_LEN1);
- }
- memcpy(aecm->channelStored, aecm->channelAdapt16, sizeof(WebRtc_Word16) * PART_LEN1);
- for (i = 0; i < PART_LEN1; i++)
- {
- aecm->channelAdapt32[i] = WEBRTC_SPL_LSHIFT_W32(
- (WebRtc_Word32)(aecm->channelAdapt16[i]), 16);
+ WebRtcAecm_InitEchoPathCore(aecm, kChannelStored16kHz);
}
memset(aecm->echoFilt, 0, sizeof(WebRtc_Word32) * PART_LEN1);
@@ -390,10 +405,6 @@
}
#endif
- aecm->mseAdaptOld = 1000;
- aecm->mseStoredOld = 1000;
- aecm->mseThreshold = WEBRTC_SPL_WORD32_MAX;
-
aecm->farEnergyMin = WEBRTC_SPL_WORD16_MAX;
aecm->farEnergyMax = WEBRTC_SPL_WORD16_MIN;
aecm->farEnergyMaxMin = 0;
@@ -410,7 +421,6 @@
memset(aecm->delayCorrelation, 0, sizeof(WebRtc_Word16) * ((CORR_MAX << 1) + 1));
aecm->startupState = 0;
- aecm->mseChannelCount = 0;
aecm->supGain = SUPGAIN_DEFAULT;
aecm->supGainOld = SUPGAIN_DEFAULT;
aecm->delayOffsetFlag = 0;
diff --git a/src/modules/audio_processing/aecm/main/source/aecm_core.h b/src/modules/audio_processing/aecm/main/source/aecm_core.h
index 5defbe4..0088e48 100644
--- a/src/modules/audio_processing/aecm/main/source/aecm_core.h
+++ b/src/modules/audio_processing/aecm/main/source/aecm_core.h
@@ -24,7 +24,7 @@
#include "typedefs.h"
#include "signal_processing_library.h"
// TODO(bjornv): Will be removed in final version.
-//#include <stdio.h>
+#include <stdio.h>
// Algorithm parameters
@@ -268,6 +268,19 @@
int WebRtcAecm_Control(AecmCore_t *aecm, int delay, int nlpFlag, int delayOffsetFlag);
///////////////////////////////////////////////////////////////////////////////////////////////
+// WebRtcAecm_InitEchoPathCore(...)
+//
+// This function resets the echo channel adaptation with the specified channel.
+// Input:
+// - aecm : Pointer to the AECM instance
+// - echo_path : Pointer to the data that should initialize the echo path
+//
+// Output:
+// - aecm : Initialized instance
+//
+void WebRtcAecm_InitEchoPathCore(AecmCore_t* aecm, const WebRtc_Word16* echo_path);
+
+///////////////////////////////////////////////////////////////////////////////////////////////
// WebRtcAecm_ProcessFrame(...)
//
// This function processes frames and sends blocks to WebRtcAecm_ProcessBlock(...)
diff --git a/src/modules/audio_processing/aecm/main/source/echo_control_mobile.c b/src/modules/audio_processing/aecm/main/source/echo_control_mobile.c
index f9d84f0..fc4c389 100644
--- a/src/modules/audio_processing/aecm/main/source/echo_control_mobile.c
+++ b/src/modules/audio_processing/aecm/main/source/echo_control_mobile.c
@@ -159,7 +159,7 @@
return 0;
}
-WebRtc_Word32 WebRtcAecm_Init(void *aecmInst, WebRtc_Word32 sampFreq, WebRtc_Word32 scSampFreq)
+WebRtc_Word32 WebRtcAecm_Init(void *aecmInst, WebRtc_Word32 sampFreq)
{
aecmob_t *aecm = aecmInst;
AecmConfig aecConfig;
@@ -176,13 +176,6 @@
}
aecm->sampFreq = sampFreq;
- if (scSampFreq < 1 || scSampFreq > 96000)
- {
- aecm->lastError = AECM_BAD_PARAMETER_ERROR;
- return -1;
- }
- aecm->scSampFreq = scSampFreq;
-
// Initialize AECM core
if (WebRtcAecm_InitCore(aecm->aecmCore, aecm->sampFreq) == -1)
{
@@ -627,6 +620,68 @@
return 0;
}
+WebRtc_Word32 WebRtcAecm_InitEchoPath(void* aecmInst,
+ const void* echo_path,
+ int size_bytes)
+{
+ aecmob_t *aecm = aecmInst;
+ const WebRtc_Word16* echo_path_ptr = echo_path;
+
+ if ((aecm == NULL) || (echo_path == NULL))
+ {
+ aecm->lastError = AECM_NULL_POINTER_ERROR;
+ return -1;
+ }
+ if (size_bytes != WebRtcAecm_echo_path_size_bytes())
+ {
+ // Input channel size does not match the size of AECM
+ aecm->lastError = AECM_BAD_PARAMETER_ERROR;
+ return -1;
+ }
+ if (aecm->initFlag != kInitCheck)
+ {
+ aecm->lastError = AECM_UNINITIALIZED_ERROR;
+ return -1;
+ }
+
+ WebRtcAecm_InitEchoPathCore(aecm->aecmCore, echo_path_ptr);
+
+ return 0;
+}
+
+WebRtc_Word32 WebRtcAecm_GetEchoPath(void* aecmInst,
+ void* echo_path,
+ int size_bytes)
+{
+ aecmob_t *aecm = aecmInst;
+ WebRtc_Word16* echo_path_ptr = echo_path;
+
+ if ((aecm == NULL) || (echo_path == NULL))
+ {
+ aecm->lastError = AECM_NULL_POINTER_ERROR;
+ return -1;
+ }
+ if (size_bytes != WebRtcAecm_echo_path_size_bytes())
+ {
+ // Input channel size does not match the size of AECM
+ aecm->lastError = AECM_BAD_PARAMETER_ERROR;
+ return -1;
+ }
+ if (aecm->initFlag != kInitCheck)
+ {
+ aecm->lastError = AECM_UNINITIALIZED_ERROR;
+ return -1;
+ }
+
+ memcpy(echo_path_ptr, aecm->aecmCore->channelStored, size_bytes);
+ return 0;
+}
+
+int WebRtcAecm_echo_path_size_bytes()
+{
+ return (PART_LEN1 * sizeof(WebRtc_Word16));
+}
+
WebRtc_Word32 WebRtcAecm_get_version(WebRtc_Word8 *versionStr, WebRtc_Word16 len)
{
const char version[] = "AECM 1.2.0";
diff --git a/src/modules/audio_processing/main/interface/audio_processing.h b/src/modules/audio_processing/main/interface/audio_processing.h
index dc9c232..4d5df2f 100644
--- a/src/modules/audio_processing/main/interface/audio_processing.h
+++ b/src/modules/audio_processing/main/interface/audio_processing.h
@@ -356,6 +356,20 @@
virtual int enable_comfort_noise(bool enable) = 0;
virtual bool is_comfort_noise_enabled() const = 0;
+ // A typical use case is to initialize the component with an echo path from a
+ // previous call. The echo path is retrieved using |GetEchoPath()| typically
+ // at the end of a call. The data can then be stored for later use as
+ // initializer, using |SetEchoPath()|.
+ // Controlling the echo path this way requires the data |size_bytes| to match
+ // the internal echo path size. This size can be acquired using
+ // |echo_path_size_bytes()|. |SetEchoPath()| causes an entire reset, worth
+ // noting if it is to be called during an ongoing call. It is possible that
+ // version incompatibilities may result in a stored echo path of the
+ // incorrect size. In this case, the stored path should be discarded.
+ virtual int SetEchoPath(const void* echo_path, int size_bytes) = 0;
+ virtual int GetEchoPath(void* echo_path, int size_bytes) const = 0;
+ virtual const int echo_path_size_bytes() const = 0;
+
protected:
virtual ~EchoControlMobile() {};
};
diff --git a/src/modules/audio_processing/main/source/echo_control_mobile_impl.cc b/src/modules/audio_processing/main/source/echo_control_mobile_impl.cc
index 1cd2502..ce940eb 100644
--- a/src/modules/audio_processing/main/source/echo_control_mobile_impl.cc
+++ b/src/modules/audio_processing/main/source/echo_control_mobile_impl.cc
@@ -11,6 +11,7 @@
#include "echo_control_mobile_impl.h"
#include <cassert>
+#include <cstring>
#include "critical_section_wrapper.h"
#include "echo_control_mobile.h"
@@ -44,14 +45,15 @@
switch (err) {
case AECM_UNSUPPORTED_FUNCTION_ERROR:
return AudioProcessing::kUnsupportedFunctionError;
+ case AECM_NULL_POINTER_ERROR:
+ return AudioProcessing::kNullPointerError;
case AECM_BAD_PARAMETER_ERROR:
return AudioProcessing::kBadParameterError;
case AECM_BAD_PARAMETER_WARNING:
return AudioProcessing::kBadStreamParameterWarning;
default:
- // AECMOBFIX_UNSPECIFIED_ERROR
- // AECMOBFIX_UNINITIALIZED_ERROR
- // AECMOBFIX_NULL_POINTER_ERROR
+ // AECM_UNSPECIFIED_ERROR
+ // AECM_UNINITIALIZED_ERROR
return AudioProcessing::kUnspecifiedError;
}
}
@@ -61,9 +63,16 @@
: ProcessingComponent(apm),
apm_(apm),
routing_mode_(kSpeakerphone),
- comfort_noise_enabled_(true) {}
+ comfort_noise_enabled_(true),
+ echo_path_size_bytes_(WebRtcAecm_echo_path_size_bytes()),
+ external_echo_path_(NULL) {}
-EchoControlMobileImpl::~EchoControlMobileImpl() {}
+EchoControlMobileImpl::~EchoControlMobileImpl() {
+ if (external_echo_path_ != NULL) {
+ delete [] external_echo_path_;
+ external_echo_path_ = NULL;
+ }
+}
int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) {
if (!is_component_enabled()) {
@@ -181,6 +190,52 @@
return comfort_noise_enabled_;
}
+int EchoControlMobileImpl::SetEchoPath(const void* echo_path,
+ int size_bytes) {
+ CriticalSectionScoped crit_scoped(*apm_->crit());
+ if (echo_path == NULL) {
+ return apm_->kNullPointerError;
+ }
+ if (size_bytes != echo_path_size_bytes_) {
+ // Size mismatch
+ return apm_->kBadParameterError;
+ }
+
+ if (external_echo_path_ == NULL) {
+ external_echo_path_ = new unsigned char[size_bytes];
+ }
+ memcpy(external_echo_path_, echo_path, size_bytes);
+
+ return Initialize();
+}
+
+int EchoControlMobileImpl::GetEchoPath(void* echo_path,
+ int size_bytes) const {
+ CriticalSectionScoped crit_scoped(*apm_->crit());
+ if (echo_path == NULL) {
+ return apm_->kNullPointerError;
+ }
+ if (size_bytes != echo_path_size_bytes_) {
+ // Size mismatch
+ return apm_->kBadParameterError;
+ }
+ if (!is_component_enabled()) {
+ return apm_->kNotEnabledError;
+ }
+
+ // Get the echo path from the first channel
+ Handle* my_handle = static_cast<Handle*>(handle(0));
+ if (WebRtcAecm_GetEchoPath(my_handle, echo_path, size_bytes) != 0) {
+ return GetHandleError(my_handle);
+ }
+
+ return apm_->kNoError;
+}
+
+const int EchoControlMobileImpl::echo_path_size_bytes() const {
+ return echo_path_size_bytes_;
+}
+
int EchoControlMobileImpl::Initialize() {
if (!is_component_enabled()) {
return apm_->kNoError;
@@ -197,7 +252,7 @@
int EchoControlMobileImpl::get_version(char* version,
int version_len_bytes) const {
if (WebRtcAecm_get_version(version, version_len_bytes) != 0) {
- return apm_->kBadParameterError;
+ return apm_->kBadParameterError;
}
return apm_->kNoError;
@@ -219,10 +274,20 @@
}
int EchoControlMobileImpl::InitializeHandle(void* handle) const {
- return WebRtcAecm_Init(static_cast<Handle*>(handle),
- apm_->sample_rate_hz(),
- 48000); // Dummy value. This isn't actually
- // required by AECM.
+ assert(handle != NULL);
+ Handle* my_handle = static_cast<Handle*>(handle);
+ if (WebRtcAecm_Init(my_handle, apm_->sample_rate_hz()) != 0) {
+ return GetHandleError(my_handle);
+ }
+ if (external_echo_path_ != NULL) {
+ if (WebRtcAecm_InitEchoPath(my_handle,
+ external_echo_path_,
+ echo_path_size_bytes_) != 0) {
+ return GetHandleError(my_handle);
+ }
+ }
+
+ return apm_->kNoError;
}
int EchoControlMobileImpl::ConfigureHandle(void* handle) const {
diff --git a/src/modules/audio_processing/main/source/echo_control_mobile_impl.h b/src/modules/audio_processing/main/source/echo_control_mobile_impl.h
index 2fd6248..5e5bd4e 100644
--- a/src/modules/audio_processing/main/source/echo_control_mobile_impl.h
+++ b/src/modules/audio_processing/main/source/echo_control_mobile_impl.h
@@ -41,6 +41,9 @@
virtual RoutingMode routing_mode() const;
virtual int enable_comfort_noise(bool enable);
virtual bool is_comfort_noise_enabled() const;
+ virtual int SetEchoPath(const void* echo_path, int size_bytes);
+ virtual int GetEchoPath(void* echo_path, int size_bytes) const;
+ virtual const int echo_path_size_bytes() const;
// ProcessingComponent implementation.
virtual void* CreateHandle() const;
@@ -53,6 +56,8 @@
const AudioProcessingImpl* apm_;
RoutingMode routing_mode_;
bool comfort_noise_enabled_;
+ const int echo_path_size_bytes_;
+ unsigned char* external_echo_path_;
};
} // namespace webrtc
diff --git a/src/modules/audio_processing/main/test/process_test/process_test.cc b/src/modules/audio_processing/main/test/process_test/process_test.cc
index c62345f..2e659ec 100644
--- a/src/modules/audio_processing/main/test/process_test/process_test.cc
+++ b/src/modules/audio_processing/main/test/process_test/process_test.cc
@@ -56,6 +56,8 @@
printf(" --drift_compensation\n");
printf(" --no_drift_compensation\n");
printf("\n -aecm Echo control mobile\n");
+ printf(" --aecm_echo_path_in_file FILE");
+ printf(" --aecm_echo_path_out_file FILE");
printf("\n -agc Gain control\n");
printf(" --analog\n");
printf(" --adaptive_digital\n");
@@ -103,6 +105,8 @@
const char* near_filename = NULL;
const char* out_filename = NULL;
const char* vad_out_filename = NULL;
+ const char* aecm_echo_path_in_filename = NULL;
+ const char* aecm_echo_path_out_filename = NULL;
int32_t sample_rate_hz = 16000;
int32_t device_sample_rate_hz = 16000;
@@ -185,6 +189,16 @@
} else if (strcmp(argv[i], "-aecm") == 0) {
ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
+ } else if (strcmp(argv[i], "--aecm_echo_path_in_file") == 0) {
+ i++;
+ ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_in_file";
+ aecm_echo_path_in_filename = argv[i];
+
+ } else if (strcmp(argv[i], "--aecm_echo_path_out_file") == 0) {
+ i++;
+ ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_out_file";
+ aecm_echo_path_out_filename = argv[i];
+
} else if (strcmp(argv[i], "-agc") == 0) {
ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
@@ -323,6 +337,8 @@
FILE* delay_file = NULL;
FILE* drift_file = NULL;
FILE* vad_out_file = NULL;
+ FILE* aecm_echo_path_in_file = NULL;
+ FILE* aecm_echo_path_out_file = NULL;
if (far_filename != NULL) {
far_file = fopen(far_filename, "rb");
@@ -361,6 +377,30 @@
<< vad_out_file;
}
+ if (aecm_echo_path_in_filename != NULL) {
+ aecm_echo_path_in_file = fopen(aecm_echo_path_in_filename, "rb");
+ ASSERT_TRUE(NULL != aecm_echo_path_in_file) << "Unable to open file "
+ << aecm_echo_path_in_filename;
+
+ const int path_size = apm->echo_control_mobile()->echo_path_size_bytes();
+ unsigned char echo_path[path_size];
+ ASSERT_EQ(path_size, fread(echo_path,
+ sizeof(unsigned char),
+ path_size,
+ aecm_echo_path_in_file));
+ EXPECT_EQ(apm->kNoError,
+ apm->echo_control_mobile()->SetEchoPath(echo_path, path_size));
+ fclose(aecm_echo_path_in_file);
+ aecm_echo_path_in_file = NULL;
+ }
+
+ if (aecm_echo_path_out_filename != NULL) {
+ aecm_echo_path_out_file = fopen(aecm_echo_path_out_filename, "wb");
+ ASSERT_TRUE(NULL != aecm_echo_path_out_file) << "Unable to open file "
+ << aecm_echo_path_out_filename;
+
+ }
+
enum Events {
kInitializeEvent,
kRenderEvent,
@@ -579,6 +619,18 @@
}
}
+ if (aecm_echo_path_out_file != NULL) {
+ const int path_size = apm->echo_control_mobile()->echo_path_size_bytes();
+ unsigned char echo_path[path_size];
+ apm->echo_control_mobile()->GetEchoPath(echo_path, path_size);
+ ASSERT_EQ(path_size, fwrite(echo_path,
+ sizeof(unsigned char),
+ path_size,
+ aecm_echo_path_out_file));
+ fclose(aecm_echo_path_out_file);
+ aecm_echo_path_out_file = NULL;
+ }
+
if (verbose) {
printf("\nProcessed frames: %d (primary), %d (reverse)\n",
primary_count, reverse_count);
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
index 3a6fce5..275ac3f 100644
--- a/src/modules/audio_processing/main/test/unit_test/unit_test.cc
+++ b/src/modules/audio_processing/main/test/unit_test/unit_test.cc
@@ -638,6 +638,31 @@
EXPECT_EQ(apm_->kNoError,
apm_->echo_control_mobile()->enable_comfort_noise(true));
EXPECT_TRUE(apm_->echo_control_mobile()->is_comfort_noise_enabled());
+ // Set and get echo path
+ const int echo_path_size = apm_->echo_control_mobile()->echo_path_size_bytes();
+ unsigned char echo_path_in[echo_path_size];
+ unsigned char echo_path_out[echo_path_size];
+ EXPECT_EQ(apm_->kNullPointerError,
+ apm_->echo_control_mobile()->SetEchoPath(NULL, echo_path_size));
+ EXPECT_EQ(apm_->kNullPointerError,
+ apm_->echo_control_mobile()->GetEchoPath(NULL, echo_path_size));
+ EXPECT_EQ(apm_->kBadParameterError,
+ apm_->echo_control_mobile()->GetEchoPath(echo_path_out, 1));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_control_mobile()->GetEchoPath(echo_path_out,
+ echo_path_size));
+ for (int i = 0; i < echo_path_size; i++) {
+ echo_path_in[i] = echo_path_out[i] + 1;
+ }
+ EXPECT_EQ(apm_->kBadParameterError,
+ apm_->echo_control_mobile()->SetEchoPath(echo_path_in, 1));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_control_mobile()->SetEchoPath(echo_path_in, echo_path_size));
+ EXPECT_EQ(apm_->kNoError,
+ apm_->echo_control_mobile()->GetEchoPath(echo_path_out, echo_path_size));
+ for (int i = 0; i < echo_path_size; i++) {
+ EXPECT_EQ(echo_path_in[i], echo_path_out[i]);
+ }
// Turn AECM off
EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false));
EXPECT_FALSE(apm_->echo_control_mobile()->is_enabled());