Added delay estimation test to audio processing unit tests.

The test verifies that we get proper delay metrics when inserting delayed versions of the same file to far-end and near-end.
Failure of the test has been verified through a missmatch between AEC delay buffer size and test buffer size.
Also added a missing file rewind to another test and removed some lint warnings.

TEST=audioproc_unittest, trybots
BUG=None

Review URL: https://webrtc-codereview.appspot.com/1100004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3514 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/audio_processing/test/unit_test.cc b/webrtc/modules/audio_processing/test/unit_test.cc
index 1baa48d..61d8cef 100644
--- a/webrtc/modules/audio_processing/test/unit_test.cc
+++ b/webrtc/modules/audio_processing/test/unit_test.cc
@@ -8,21 +8,21 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "audio_processing.h"
-
 #include <stdio.h>
 
 #include <algorithm>
+#include <queue>
 
 #include "gtest/gtest.h"
 
-#include "event_wrapper.h"
-#include "module_common_types.h"
-#include "scoped_ptr.h"
-#include "signal_processing_library.h"
-#include "test/testsupport/fileutils.h"
-#include "thread_wrapper.h"
-#include "trace.h"
+#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
+#include "webrtc/modules/audio_processing/include/audio_processing.h"
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/system_wrappers/interface/event_wrapper.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+#include "webrtc/system_wrappers/interface/thread_wrapper.h"
+#include "webrtc/system_wrappers/interface/trace.h"
+#include "webrtc/test/testsupport/fileutils.h"
 #ifdef WEBRTC_ANDROID_PLATFORM_BUILD
 #include "external/webrtc/webrtc/modules/audio_processing/test/unittest.pb.h"
 #else
@@ -67,14 +67,18 @@
 const size_t kProcessSampleRatesSize = sizeof(kProcessSampleRates) /
     sizeof(*kProcessSampleRates);
 
+int TruncateToMultipleOf10(int value) {
+  return (value / 10) * 10;
+}
+
 // TODO(andrew): Use the MonoToStereo routine from AudioFrameOperations.
 void MixStereoToMono(const int16_t* stereo,
                      int16_t* mono,
                      int samples_per_channel) {
   for (int i = 0; i < samples_per_channel; i++) {
-    int32_t int32 = (static_cast<int32_t>(stereo[i * 2]) +
-                     static_cast<int32_t>(stereo[i * 2 + 1])) >> 1;
-    mono[i] = static_cast<int16_t>(int32);
+    int32_t mono_s32 = (static_cast<int32_t>(stereo[i * 2]) +
+        static_cast<int32_t>(stereo[i * 2 + 1])) >> 1;
+    mono[i] = static_cast<int16_t>(mono_s32);
   }
 }
 
@@ -231,6 +235,8 @@
   template <typename F>
   void ChangeTriggersInit(F f, AudioProcessing* ap, int initial_value,
                           int changed_value);
+  void ProcessDelayVerificationTest(int delay_ms, int system_delay_ms,
+                                    int delay_min, int delay_max);
 
   const std::string output_path_;
   const std::string ref_path_;
@@ -489,6 +495,93 @@
   EXPECT_TRUE(FrameDataAreEqual(*frame_, frame_copy));
 }
 
+void ApmTest::ProcessDelayVerificationTest(int delay_ms, int system_delay_ms,
+                                           int delay_min, int delay_max) {
+  // The |revframe_| and |frame_| should include the proper frame information,
+  // hence can be used for extracting information.
+  webrtc::AudioFrame tmp_frame;
+  std::queue<webrtc::AudioFrame*> frame_queue;
+  bool causal = true;
+
+  tmp_frame.CopyFrom(*revframe_);
+  SetFrameTo(&tmp_frame, 0);
+
+  EXPECT_EQ(apm_->kNoError, apm_->Initialize());
+  // Initialize the |frame_queue| with empty frames.
+  int frame_delay = delay_ms / 10;
+  while (frame_delay < 0) {
+    webrtc::AudioFrame* frame = new AudioFrame();
+    frame->CopyFrom(tmp_frame);
+    frame_queue.push(frame);
+    frame_delay++;
+    causal = false;
+  }
+  while (frame_delay > 0) {
+    webrtc::AudioFrame* frame = new AudioFrame();
+    frame->CopyFrom(tmp_frame);
+    frame_queue.push(frame);
+    frame_delay--;
+  }
+  // Run for 4.5 seconds, skipping statistics from the first second. We need
+  // enough frames with audio to have reliable estimates, but as few as possible
+  // to keep processing time down. 4.5 seconds seemed to be a good compromise
+  // for this recording.
+  for (int frame_count = 0; frame_count < 450; ++frame_count) {
+    webrtc::AudioFrame* frame = new AudioFrame();
+    frame->CopyFrom(tmp_frame);
+    // Use the near end recording, since that has more speech in it.
+    ASSERT_TRUE(ReadFrame(near_file_, frame));
+    frame_queue.push(frame);
+    webrtc::AudioFrame* reverse_frame = frame;
+    webrtc::AudioFrame* process_frame = frame_queue.front();
+    if (!causal) {
+      reverse_frame = frame_queue.front();
+      // When we call ProcessStream() the frame is modified, so we can't use the
+      // pointer directly when things are non-causal. Use an intermediate frame
+      // and copy the data.
+      process_frame = &tmp_frame;
+      process_frame->CopyFrom(*frame);
+    }
+    EXPECT_EQ(apm_->kNoError, apm_->AnalyzeReverseStream(reverse_frame));
+    EXPECT_EQ(apm_->kNoError, apm_->set_stream_delay_ms(system_delay_ms));
+    EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(process_frame));
+    frame = frame_queue.front();
+    frame_queue.pop();
+    delete frame;
+
+    if (frame_count == 100) {
+      int median;
+      int std;
+      // Discard the first delay metrics to avoid convergence effects.
+      EXPECT_EQ(apm_->kNoError,
+                apm_->echo_cancellation()->GetDelayMetrics(&median, &std));
+    }
+  }
+
+  rewind(near_file_);
+  while (!frame_queue.empty()) {
+    webrtc::AudioFrame* frame = frame_queue.front();
+    frame_queue.pop();
+    delete frame;
+  }
+  // Calculate expected delay estimate and acceptable regions. Further,
+  // limit them w.r.t. AEC delay estimation support.
+  const int samples_per_ms = std::min(16, frame_->samples_per_channel_ / 10);
+  int expected_median = std::min(std::max(delay_ms - system_delay_ms,
+                                          delay_min), delay_max);
+  int expected_median_high = std::min(std::max(
+      expected_median + 96 / samples_per_ms, delay_min), delay_max);
+  int expected_median_low = std::min(std::max(
+      expected_median - 96 / samples_per_ms, delay_min), delay_max);
+  // Verify delay metrics.
+  int median;
+  int std;
+  EXPECT_EQ(apm_->kNoError,
+            apm_->echo_cancellation()->GetDelayMetrics(&median, &std));
+  EXPECT_GE(expected_median_high, median);
+  EXPECT_LE(expected_median_low, median);
+}
+
 TEST_F(ApmTest, StreamParameters) {
   // No errors when the components are disabled.
   EXPECT_EQ(apm_->kNoError,
@@ -719,10 +812,79 @@
   EXPECT_FALSE(apm_->echo_cancellation()->is_enabled());
 }
 
+TEST_F(ApmTest, EchoCancellationReportsCorrectDelays) {
+  // Enable AEC only.
+  EXPECT_EQ(apm_->kNoError,
+            apm_->echo_cancellation()->enable_drift_compensation(false));
+  EXPECT_EQ(apm_->kNoError,
+            apm_->echo_cancellation()->enable_metrics(false));
+  EXPECT_EQ(apm_->kNoError,
+            apm_->echo_cancellation()->enable_delay_logging(true));
+  EXPECT_EQ(apm_->kNoError, apm_->echo_cancellation()->Enable(true));
+
+  // Internally in the AEC the amount of lookahead the delay estimation can
+  // handle is 15 blocks and the maximum delay is set to 60 blocks.
+  const int kLookaheadBlocks = 15;
+  const int kMaxDelayBlocks = 60;
+  // The AEC has a startup time before it actually starts to process. This
+  // procedure can flush the internal far-end buffer, which of course affects
+  // the delay estimation. Therefore, we set a system_delay high enough to
+  // avoid that. The smallest system_delay you can report without flushing the
+  // buffer is 66 ms in 8 kHz.
+  //
+  // It is known that for 16 kHz (and 32 kHz) sampling frequency there is an
+  // additional stuffing of 8 ms on the fly, but it seems to have no impact on
+  // delay estimation. This should be noted though. In case of test failure,
+  // this could be the cause.
+  const int kSystemDelayMs = 66;
+  // Test a couple of corner cases and verify that the estimated delay is
+  // within a valid region (set to +-1.5 blocks). Note that these cases are
+  // sampling frequency dependent.
+  for (size_t i = 0; i < kProcessSampleRatesSize; i++) {
+    Init(kProcessSampleRates[i], 2, 2, 2, false);
+    // Sampling frequency dependent variables.
+    const int num_ms_per_block = std::max(4,
+                                           640 / frame_->samples_per_channel_);
+    const int delay_min_ms = -kLookaheadBlocks * num_ms_per_block;
+    const int delay_max_ms = (kMaxDelayBlocks - 1) * num_ms_per_block;
+
+    // 1) Verify correct delay estimate at lookahead boundary.
+    int delay_ms = TruncateToMultipleOf10(kSystemDelayMs + delay_min_ms);
+    ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms,
+                                 delay_max_ms);
+    // 2) A delay less than maximum lookahead should give an delay estimate at
+    //    the boundary (= -kLookaheadBlocks * num_ms_per_block).
+    delay_ms -= 20;
+    ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms,
+                                 delay_max_ms);
+    // 3) Three values around zero delay. Note that we need to compensate for
+    //    the fake system_delay.
+    delay_ms = TruncateToMultipleOf10(kSystemDelayMs - 10);
+    ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms,
+                                 delay_max_ms);
+    delay_ms = TruncateToMultipleOf10(kSystemDelayMs);
+    ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms,
+                                 delay_max_ms);
+    delay_ms = TruncateToMultipleOf10(kSystemDelayMs + 10);
+    ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms,
+                                 delay_max_ms);
+    // 4) Verify correct delay estimate at maximum delay boundary.
+    delay_ms = TruncateToMultipleOf10(kSystemDelayMs + delay_max_ms);
+    ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms,
+                                 delay_max_ms);
+    // 5) A delay above the maximum delay should give an estimate at the
+    //    boundary (= (kMaxDelayBlocks - 1) * num_ms_per_block).
+    delay_ms += 20;
+    ProcessDelayVerificationTest(delay_ms, kSystemDelayMs, delay_min_ms,
+                                 delay_max_ms);
+  }
+}
+
 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_->kBadSampleRateError,
+            apm_->echo_control_mobile()->Enable(true));
   // Turn AECM on (and AEC off)
   Init(16000, 2, 2, 2, false);
   EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(true));
@@ -965,7 +1127,7 @@
 
   // Min value if energy_ == 0.
   SetFrameTo(frame_, 10000);
-  uint32_t energy = frame_->energy_; // Save default to restore below.
+  uint32_t energy = frame_->energy_;  // Save default to restore below.
   frame_->energy_ = 0;
   EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
   EXPECT_EQ(apm_->kNoError, apm_->ProcessStream(frame_));
@@ -1095,6 +1257,8 @@
   for (size_t i = 0; i < kProcessSampleRatesSize; i++) {
     Init(kProcessSampleRates[i], 2, 2, 2, false);
     int analog_level = 127;
+    EXPECT_EQ(0, feof(far_file_));
+    EXPECT_EQ(0, feof(near_file_));
     while (1) {
       if (!ReadFrame(far_file_, revframe_)) break;
       CopyLeftToRightChannel(revframe_->data_, revframe_->samples_per_channel_);
@@ -1115,6 +1279,8 @@
 
       VerifyChannelsAreEqual(frame_->data_, frame_->samples_per_channel_);
     }
+    rewind(far_file_);
+    rewind(near_file_);
   }
 }