Optimize minimum delay in blocker

Could not hear any difference when running the beamformer_test, although sample-wise it changes because of the non-linear character of the processing.

R=andrew@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@8051 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/common_audio/blocker.cc b/webrtc/common_audio/blocker.cc
index 3b92364..400db0c 100644
--- a/webrtc/common_audio/blocker.cc
+++ b/webrtc/common_audio/blocker.cc
@@ -83,6 +83,16 @@
   }
 }
 
+int gcd(int a, int b) {
+  int tmp;
+  while (b) {
+     tmp = a;
+     a = b;
+     b = tmp % b;
+  }
+  return a;
+}
+
 }  // namespace
 
 namespace webrtc {
@@ -98,7 +108,7 @@
       block_size_(block_size),
       num_input_channels_(num_input_channels),
       num_output_channels_(num_output_channels),
-      initial_delay_(block_size_),
+      initial_delay_(block_size_ - gcd(chunk_size, shift_amount)),
       frame_offset_(0),
       input_buffer_(chunk_size_ + initial_delay_, num_input_channels_),
       output_buffer_(chunk_size_ + initial_delay_, num_output_channels_),
diff --git a/webrtc/common_audio/blocker.h b/webrtc/common_audio/blocker.h
index 1cf95db..68289d5 100644
--- a/webrtc/common_audio/blocker.h
+++ b/webrtc/common_audio/blocker.h
@@ -80,8 +80,6 @@
   const int num_output_channels_;
 
   // The number of frames of delay to add at the beginning of the first chunk.
-  //
-  // TODO(claguna): find a lower cap for this than |block_size_|.
   const int initial_delay_;
 
   // The frame index into the input buffer where the first block should be read
diff --git a/webrtc/common_audio/blocker_unittest.cc b/webrtc/common_audio/blocker_unittest.cc
index 5009c0e..d1bfa69 100644
--- a/webrtc/common_audio/blocker_unittest.cc
+++ b/webrtc/common_audio/blocker_unittest.cc
@@ -15,7 +15,7 @@
 namespace {
 
 // Callback Function to add 3 to every sample in the signal.
-class SimpleBlockerCallback : public webrtc::BlockerCallback {
+class PlusThreeBlockerCallback : public webrtc::BlockerCallback {
  public:
   virtual void ProcessBlock(const float* const* input,
                             int num_frames,
@@ -30,6 +30,22 @@
   }
 };
 
+// No-op Callback Function.
+class CopyBlockerCallback : public webrtc::BlockerCallback {
+ public:
+  virtual void ProcessBlock(const float* const* input,
+                            int num_frames,
+                            int num_input_channels,
+                            int num_output_channels,
+                            float* const* output) OVERRIDE {
+    for (int i = 0; i < num_output_channels; ++i) {
+      for (int j = 0; j < num_frames; ++j) {
+        output[i][j] = input[i][j];
+      }
+    }
+  }
+};
+
 }  // namespace
 
 namespace webrtc {
@@ -75,6 +91,21 @@
     }
   }
 
+  void ValidateInitialDelay(const float* const* output,
+                            int num_channels,
+                            int num_frames,
+                            int initial_delay) {
+    for (int i = 0; i < num_channels; ++i) {
+      for (int j = 0; j < num_frames; ++j) {
+        if (j < initial_delay) {
+          EXPECT_FLOAT_EQ(output[i][j], 0.f);
+        } else {
+          EXPECT_GT(output[i][j], 0.f);
+        }
+      }
+    }
+  }
+
   static void CopyTo(float* const* dst,
                      int start_index_dst,
                      int start_index_src,
@@ -104,8 +135,8 @@
   const ChannelBuffer<float> input_cb(kInput[0], kNumFrames, kNumInputChannels);
 
   const float kExpectedOutput[kNumInputChannels][kNumFrames] = {
-      {6, 6, 12, 12, 20, 20, 20, 20, 20, 20},
-      {6, 6, 12, 12, 28, 28, 28, 28, 28, 28}};
+      {6, 6, 12, 20, 20, 20, 20, 20, 20, 20},
+      {6, 6, 12, 28, 28, 28, 28, 28, 28, 28}};
   const ChannelBuffer<float> expected_output_cb(
       kExpectedOutput[0], kNumFrames, kNumInputChannels);
 
@@ -115,7 +146,7 @@
   ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels);
   ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels);
 
-  SimpleBlockerCallback callback;
+  PlusThreeBlockerCallback callback;
   Blocker blocker(kChunkSize,
                   kBlockSize,
                   kNumInputChannels,
@@ -154,11 +185,11 @@
       {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}};
   const ChannelBuffer<float> input_cb(kInput[0], kNumFrames, kNumInputChannels);
 
-  const float kExpectedOutput[kNumInputChannels][kNumFrames] = {
-      {6, 6, 6, 12, 10, 10, 20, 10, 10, 20, 10, 10},
-      {6, 6, 6, 12, 14, 14, 28, 14, 14, 28, 14, 14}};
+  const float kExpectedOutput[kNumOutputChannels][kNumFrames] = {
+      {6, 10, 10, 20, 10, 10, 20, 10, 10, 20, 10, 10},
+      {6, 14, 14, 28, 14, 14, 28, 14, 14, 28, 14, 14}};
   const ChannelBuffer<float> expected_output_cb(
-      kExpectedOutput[0], kNumFrames, kNumInputChannels);
+      kExpectedOutput[0], kNumFrames, kNumOutputChannels);
 
   const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
 
@@ -166,7 +197,7 @@
   ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels);
   ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels);
 
-  SimpleBlockerCallback callback;
+  PlusThreeBlockerCallback callback;
   Blocker blocker(kChunkSize,
                   kBlockSize,
                   kNumInputChannels,
@@ -205,11 +236,11 @@
       {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3}};
   const ChannelBuffer<float> input_cb(kInput[0], kNumFrames, kNumInputChannels);
 
-  const float kExpectedOutput[kNumInputChannels][kNumFrames] = {
-      {6, 6, 6, 6, 10, 10, 10, 10, 10, 10, 10, 10},
-      {6, 6, 6, 6, 14, 14, 14, 14, 14, 14, 14, 14}};
+  const float kExpectedOutput[kNumOutputChannels][kNumFrames] = {
+      {10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
+      {14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}};
   const ChannelBuffer<float> expected_output_cb(
-      kExpectedOutput[0], kNumFrames, kNumInputChannels);
+      kExpectedOutput[0], kNumFrames, kNumOutputChannels);
 
   const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
 
@@ -217,7 +248,7 @@
   ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels);
   ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels);
 
-  SimpleBlockerCallback callback;
+  PlusThreeBlockerCallback callback;
   Blocker blocker(kChunkSize,
                   kBlockSize,
                   kNumInputChannels,
@@ -242,4 +273,63 @@
                          kNumFrames);
 }
 
+TEST_F(BlockerTest, InitialDelaysAreMinimum) {
+  const int kNumInputChannels = 3;
+  const int kNumOutputChannels = 2;
+  const int kNumFrames = 1280;
+  const int kChunkSize[] =
+      {80, 80, 80, 80, 80, 80, 160, 160, 160, 160, 160, 160};
+  const int kBlockSize[] =
+      {64, 64, 64, 128, 128, 128, 128, 128, 128, 256, 256, 256};
+  const int kShiftAmount[] =
+      {16, 32, 64, 32, 64, 128, 32, 64, 128, 64, 128, 256};
+  const int kInitialDelay[] =
+      {48, 48, 48, 112, 112, 112, 96, 96, 96, 224, 224, 224};
+
+  float input[kNumInputChannels][kNumFrames];
+  for (int i = 0; i < kNumInputChannels; ++i) {
+    for (int j = 0; j < kNumFrames; ++j) {
+      input[i][j] = i + 1;
+    }
+  }
+  const ChannelBuffer<float> input_cb(input[0], kNumFrames, kNumInputChannels);
+
+  ChannelBuffer<float> output_cb(kNumFrames, kNumOutputChannels);
+
+  CopyBlockerCallback callback;
+
+  for (size_t i = 0; i < (sizeof(kChunkSize) / sizeof(*kChunkSize)); ++i) {
+    scoped_ptr<float[]> window(new float[kBlockSize[i]]);
+    for (int j = 0; j < kBlockSize[i]; ++j) {
+      window[j] = 1.f;
+    }
+
+    ChannelBuffer<float> input_chunk_cb(kChunkSize[i], kNumInputChannels);
+    ChannelBuffer<float> output_chunk_cb(kChunkSize[i], kNumOutputChannels);
+
+    Blocker blocker(kChunkSize[i],
+                    kBlockSize[i],
+                    kNumInputChannels,
+                    kNumOutputChannels,
+                    window.get(),
+                    kShiftAmount[i],
+                    &callback);
+
+    RunTest(&blocker,
+            kChunkSize[i],
+            kNumFrames,
+            input_cb.channels(),
+            input_chunk_cb.channels(),
+            output_cb.channels(),
+            output_chunk_cb.channels(),
+            kNumInputChannels,
+            kNumOutputChannels);
+
+    ValidateInitialDelay(output_cb.channels(),
+                         kNumOutputChannels,
+                         kNumFrames,
+                         kInitialDelay[i]);
+  }
+}
+
 }  // namespace webrtc
diff --git a/webrtc/common_audio/lapped_transform_unittest.cc b/webrtc/common_audio/lapped_transform_unittest.cc
index 9052382..4b521fa 100644
--- a/webrtc/common_audio/lapped_transform_unittest.cc
+++ b/webrtc/common_audio/lapped_transform_unittest.cc
@@ -53,12 +53,7 @@
     float full_length = (frames - 1) * 2;
     ++block_num_;
 
-    if (block_num_ == 1) {
-      for (int i = 0; i < frames; ++i) {
-        ASSERT_NEAR(in_block[0][i].real(), 0.0f, 1e-5f);
-        ASSERT_NEAR(in_block[0][i].imag(), 0.0f, 1e-5f);
-      }
-    } else {
+    if (block_num_ > 0) {
       ASSERT_NEAR(in_block[0][0].real(), full_length, 1e-5f);
       ASSERT_NEAR(in_block[0][0].imag(), 0.0f, 1e-5f);
       for (int i = 1; i < frames; ++i) {
@@ -114,7 +109,7 @@
 
   for (int i = 0; i < kChannels; ++i) {
     for (int j = 0; j < kChunkLength; ++j) {
-      ASSERT_NEAR(out_chunk[i][j], (j < kBlockLength) ? 0.0f : 2.0f, 1e-5f);
+      ASSERT_NEAR(out_chunk[i][j], 2.0f, 1e-5f);
     }
   }
 
@@ -145,7 +140,9 @@
   trans.ProcessChunk(&in_chunk, &out_chunk);
 
   for (int i = 0; i < kChunkLength; ++i) {
-    ASSERT_NEAR(out_chunk[i], (i < kBlockLength) ? 0.0f : 2.0f, 1e-5f);
+    ASSERT_NEAR(out_chunk[i],
+                (i < kBlockLength - kShiftAmount) ? 0.0f : 2.0f,
+                1e-5f);
   }
 
   ASSERT_EQ(kChunkLength / kShiftAmount, noop.block_num());