Import LappedTransform and friends.

Add code for doing block-based frequency domain processing. Developed
and reviewed in isolation. Corresponding export CL:
https://chromereviews.googleplex.com/95187013/

R=bercic@google.com, kjellander@webrtc.org, turaj@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@7359 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/common_audio/BUILD.gn b/common_audio/BUILD.gn
index adee4b2..36740e8 100644
--- a/common_audio/BUILD.gn
+++ b/common_audio/BUILD.gn
@@ -20,6 +20,8 @@
 source_set("common_audio") {
   sources = [
     "audio_util.cc",
+    "blocker.cc",
+    "blocker.h",
     "fir_filter.cc",
     "fir_filter.h",
     "fir_filter_neon.h",
@@ -82,10 +84,28 @@
     "wav_header.h",
     "wav_writer.cc",
     "wav_writer.h",
+    "window_generator.cc",
+    "window_generator.h",
   ]
 
   deps = [ "../system_wrappers" ]
 
+  # TODO(ajm): Enable when GN support for openmax_dl is added.
+  # See: crbug.com/419206
+  # Not needed immediately, since nothing built by GN depends on these bits.
+  # TODO(ajm): Workaround until openmax_dl has non-Android ARM support.
+  # See: crbug.com/415393
+  #if (cpu_arch != "arm" or (cpu_arch == "arm" and is_android)) {
+  #  'sources' += [
+  #    'lapped_transform.cc',
+  #    'lapped_transform.h',
+  #    'real_fourier.cc',
+  #    'real_fourier.h',
+  #  ]
+  #
+  #  deps += [ "//third_party/openmax_dl/dl" ]
+  #}
+
   if (cpu_arch == "arm") {
     sources += [
       "signal_processing/complex_bit_reverse_arm.S",
diff --git a/common_audio/blocker.cc b/common_audio/blocker.cc
new file mode 100644
index 0000000..d207c1b
--- /dev/null
+++ b/common_audio/blocker.cc
@@ -0,0 +1,220 @@
+/*
+ *  Copyright (c) 2014 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 "webrtc/common_audio/blocker.h"
+
+#include <string.h>
+
+#include "webrtc/base/checks.h"
+
+namespace {
+
+// Adds |a| and |b| frame by frame into |result| (basically matrix addition).
+void AddFrames(const float* const* a,
+               int a_start_index,
+               const float* const* b,
+               int b_start_index,
+               int num_frames,
+               int num_channels,
+               float* const* result,
+               int result_start_index) {
+  for (int i = 0; i < num_channels; ++i) {
+    for (int j = 0; j < num_frames; ++j) {
+      result[i][j + result_start_index] =
+          a[i][j + a_start_index] + b[i][j + b_start_index];
+    }
+  }
+}
+
+// Copies |src| into |dst| channel by channel.
+void CopyFrames(const float* const* src,
+                int src_start_index,
+                int num_frames,
+                int num_channels,
+                float* const* dst,
+                int dst_start_index) {
+  for (int i = 0; i < num_channels; ++i) {
+    memcpy(&dst[i][dst_start_index],
+           &src[i][src_start_index],
+           num_frames * sizeof(float));
+  }
+}
+
+void ZeroOut(float* const* buffer,
+             int starting_idx,
+             int num_frames,
+             int num_channels) {
+  for (int i = 0; i < num_channels; ++i) {
+    memset(&buffer[i][starting_idx], 0, num_frames * sizeof(float));
+  }
+}
+
+// Pointwise multiplies each channel of |frames| with |window|. Results are
+// stored in |frames|.
+void ApplyWindow(const float* window,
+                 int num_frames,
+                 int num_channels,
+                 float* const* frames) {
+  for (int i = 0; i < num_channels; ++i) {
+    for (int j = 0; j < num_frames; ++j) {
+      frames[i][j] = frames[i][j] * window[j];
+    }
+  }
+}
+
+}  // namespace
+
+namespace webrtc {
+
+Blocker::Blocker(int chunk_size,
+                 int block_size,
+                 int num_input_channels,
+                 int num_output_channels,
+                 const float* window,
+                 int shift_amount,
+                 BlockerCallback* callback)
+    : chunk_size_(chunk_size),
+      block_size_(block_size),
+      num_input_channels_(num_input_channels),
+      num_output_channels_(num_output_channels),
+      initial_delay_(block_size_),
+      frame_offset_(0),
+      input_buffer_(chunk_size_ + initial_delay_, num_input_channels_),
+      output_buffer_(chunk_size_ + initial_delay_, num_output_channels_),
+      input_block_(block_size_, num_input_channels_),
+      output_block_(block_size_, num_output_channels_),
+      window_(new float[block_size_]),
+      shift_amount_(shift_amount),
+      callback_(callback) {
+  CHECK_LE(num_output_channels_, num_input_channels_);
+  CHECK_GE(chunk_size_, block_size_);
+
+  memcpy(window_.get(), window, block_size_ * sizeof(float));
+  size_t buffer_size = chunk_size_ + initial_delay_;
+  memset(input_buffer_.channels()[0],
+         0,
+         buffer_size * num_input_channels_ * sizeof(float));
+  memset(output_buffer_.channels()[0],
+         0,
+         buffer_size * num_output_channels_ * sizeof(float));
+}
+
+// Both the input and output buffers look like this:
+//
+//                      delay*             chunk_size    chunk_size + delay*
+//  buffer: <-------------|---------------------|---------------|>
+//                _a_              _b_                 _c_
+//
+// On each call to ProcessChunk():
+// 1. New input gets read into sections _b_ and _c_ of the input buffer.
+// 2. We block starting from frame_offset.
+// 3. We block until we reach a block |bl| that doesn't contain any frames
+//    from sections _a_ or _b_ of the input buffer.
+// 4. We window the current block, fire the callback for processing, window
+//    again, and overlap/add to the output buffer.
+// 5. We copy sections _a_ and _b_ of the output buffer into output.
+// 6. For both the input and the output buffers, we copy section c into
+//    section a.
+// 7. We set the new frame_offset to be the difference between the first frame
+//    of |bl| and the border between sections _b_ and _c_.
+//
+// * delay here refers to inintial_delay_
+//
+// TODO(claguna): Look at using ring buffers to eliminate some copies.
+void Blocker::ProcessChunk(const float* const* input,
+                           int chunk_size,
+                           int num_input_channels,
+                           int num_output_channels,
+                           float* const* output) {
+  CHECK_EQ(chunk_size, chunk_size_);
+  CHECK_EQ(num_input_channels, num_input_channels_);
+  CHECK_EQ(num_output_channels, num_output_channels_);
+
+  // Copy new data into input buffer at
+  // [|initial_delay_|, |chunk_size_| + |initial_delay_|].
+  CopyFrames(input,
+             0,
+             chunk_size_,
+             num_input_channels_,
+             input_buffer_.channels(),
+             initial_delay_);
+
+  int first_frame_in_block = frame_offset_;
+
+  // Loop through blocks.
+  while (first_frame_in_block < chunk_size_) {
+    CopyFrames(input_buffer_.channels(),
+               first_frame_in_block,
+               block_size_,
+               num_input_channels_,
+               input_block_.channels(),
+               0);
+
+    ApplyWindow(window_.get(),
+                block_size_,
+                num_input_channels_,
+                input_block_.channels());
+    callback_->ProcessBlock(input_block_.channels(),
+                            block_size_,
+                            num_input_channels_,
+                            num_output_channels_,
+                            output_block_.channels());
+    ApplyWindow(window_.get(),
+                block_size_,
+                num_output_channels_,
+                output_block_.channels());
+
+    AddFrames(output_buffer_.channels(),
+              first_frame_in_block,
+              output_block_.channels(),
+              0,
+              block_size_,
+              num_output_channels_,
+              output_buffer_.channels(),
+              first_frame_in_block);
+
+    first_frame_in_block += shift_amount_;
+  }
+
+  // Copy output buffer to output
+  CopyFrames(output_buffer_.channels(),
+             0,
+             chunk_size_,
+             num_output_channels_,
+             output,
+             0);
+
+  // Copy input buffer [chunk_size_, chunk_size_ + initial_delay]
+  // to input buffer [0, initial_delay]
+  CopyFrames(input_buffer_.channels(),
+             chunk_size,
+             initial_delay_,
+             num_input_channels_,
+             input_buffer_.channels(),
+             0);
+
+  // Copy output buffer [chunk_size_, chunk_size_ + initial_delay]
+  // to output buffer [0, initial_delay], zero the rest.
+  CopyFrames(output_buffer_.channels(),
+             chunk_size,
+             initial_delay_,
+             num_output_channels_,
+             output_buffer_.channels(),
+             0);
+  ZeroOut(output_buffer_.channels(),
+          initial_delay_,
+          chunk_size_,
+          num_output_channels_);
+
+  // Calculate new starting frames.
+  frame_offset_ = first_frame_in_block - chunk_size_;
+}
+
+}  // namespace webrtc
diff --git a/common_audio/blocker.h b/common_audio/blocker.h
new file mode 100644
index 0000000..f3f99cd
--- /dev/null
+++ b/common_audio/blocker.h
@@ -0,0 +1,119 @@
+/*
+ *  Copyright (c) 2014 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.
+ */
+
+#ifndef WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_
+#define WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_
+
+#include "webrtc/modules/audio_processing/common.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+// The callback function to process audio in the time domain. Input has already
+// been windowed, and output will be windowed. The number of input channels
+// must be >= the number of output channels.
+class BlockerCallback {
+ public:
+  virtual ~BlockerCallback() {}
+
+  virtual void ProcessBlock(const float* const* input,
+                            int num_frames,
+                            int num_input_channels,
+                            int num_output_channels,
+                            float* const* output) = 0;
+};
+
+// The main purpose of Blocker is to abstract away the fact that often we
+// receive a different number of audio frames than our transform takes. For
+// example, most FFTs work best when the fft-size is a power of 2, but suppose
+// we receive 20ms of audio at a sample rate of 48000. That comes to 960 frames
+// of audio, which is not a power of 2. Blocker allows us to specify the
+// transform and all other necessary processing via the Process() callback
+// function without any constraints on the transform-size
+// (read: |block_size_|) or received-audio-size (read: |chunk_size_|).
+// We handle this for the multichannel audio case, allowing for different
+// numbers of input and output channels (for example, beamforming takes 2 or
+// more input channels and returns 1 output channel). Audio signals are
+// represented as deinterleaved floats in the range [-1, 1].
+//
+// Blocker is responsible for:
+// - blocking audio while handling potential discontinuities on the edges
+//   of chunks
+// - windowing blocks before sending them to Process()
+// - windowing processed blocks, and overlap-adding them together before
+//   sending back a processed chunk
+//
+// To use blocker:
+// 1. Impelment a BlockerCallback object |bc|.
+// 2. Instantiate a Blocker object |b|, passing in |bc|.
+// 3. As you receive audio, call b.ProcessChunk() to get processed audio.
+//
+// A small amount of delay is added to the first received chunk to deal with
+// the difference in chunk/block sizes. This delay is <= chunk_size.
+class Blocker {
+ public:
+  Blocker(int chunk_size,
+          int block_size,
+          int num_input_channels,
+          int num_output_channels,
+          const float* window,
+          int shift_amount,
+          BlockerCallback* callback);
+
+  void ProcessChunk(const float* const* input,
+                    int num_frames,
+                    int num_input_channels,
+                    int num_output_channels,
+                    float* const* output);
+
+ private:
+  const int chunk_size_;
+  const int block_size_;
+  const int num_input_channels_;
+  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
+  // from. This is necessary because shift_amount_ is not necessarily a
+  // multiple of chunk_size_, so blocks won't line up at the start of the
+  // buffer.
+  int frame_offset_;
+
+  // Since blocks nearly always overlap, there are certain blocks that require
+  // frames from the end of one chunk and the beginning of the next chunk. The
+  // input and output buffers are responsible for saving those frames between
+  // calls to ProcessChunk().
+  //
+  // Both contain |initial delay| + |chunk_size| frames.
+  ChannelBuffer<float> input_buffer_;
+  ChannelBuffer<float> output_buffer_;
+
+  // Space for the input block (can't wrap because of windowing).
+  ChannelBuffer<float> input_block_;
+
+  // Space for the output block (can't wrap because of overlap/add).
+  ChannelBuffer<float> output_block_;
+
+  scoped_ptr<float[]> window_;
+
+  // The amount of frames between the start of contiguous blocks. For example,
+  // |shift_amount_| = |block_size_| / 2 for a Hann window.
+  int shift_amount_;
+
+  BlockerCallback* callback_;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_INTERNAL_BEAMFORMER_BLOCKER_H_
diff --git a/common_audio/blocker_unittest.cc b/common_audio/blocker_unittest.cc
new file mode 100644
index 0000000..5009c0e
--- /dev/null
+++ b/common_audio/blocker_unittest.cc
@@ -0,0 +1,245 @@
+/*
+ *  Copyright (c) 2014 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 "webrtc/common_audio/blocker.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+// Callback Function to add 3 to every sample in the signal.
+class SimpleBlockerCallback : 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] + 3;
+      }
+    }
+  }
+};
+
+}  // namespace
+
+namespace webrtc {
+
+// Tests blocking with a window that multiplies the signal by 2, a callback
+// that adds 3 to each sample in the signal, and different combinations of chunk
+// size, block size, and shift amount.
+class BlockerTest : public ::testing::Test {
+ protected:
+  void RunTest(Blocker* blocker,
+               int chunk_size,
+               int num_frames,
+               const float* const* input,
+               float* const* input_chunk,
+               float* const* output,
+               float* const* output_chunk,
+               int num_input_channels,
+               int num_output_channels) {
+    int start = 0;
+    int end = chunk_size - 1;
+    while (end < num_frames) {
+      CopyTo(input_chunk, 0, start, num_input_channels, chunk_size, input);
+      blocker->ProcessChunk(input_chunk,
+                            chunk_size,
+                            num_input_channels,
+                            num_output_channels,
+                            output_chunk);
+      CopyTo(output, start, 0, num_output_channels, chunk_size, output_chunk);
+
+      start = start + chunk_size;
+      end = end + chunk_size;
+    }
+  }
+
+  void ValidateSignalEquality(const float* const* expected,
+                              const float* const* actual,
+                              int num_channels,
+                              int num_frames) {
+    for (int i = 0; i < num_channels; ++i) {
+      for (int j = 0; j < num_frames; ++j) {
+        EXPECT_FLOAT_EQ(expected[i][j], actual[i][j]);
+      }
+    }
+  }
+
+  static void CopyTo(float* const* dst,
+                     int start_index_dst,
+                     int start_index_src,
+                     int num_channels,
+                     int num_frames,
+                     const float* const* src) {
+    for (int i = 0; i < num_channels; ++i) {
+      memcpy(&dst[i][start_index_dst],
+             &src[i][start_index_src],
+             num_frames * sizeof(float));
+    }
+  }
+};
+
+TEST_F(BlockerTest, TestBlockerMutuallyPrimeChunkandBlockSize) {
+  const int kNumInputChannels = 3;
+  const int kNumOutputChannels = 2;
+  const int kNumFrames = 10;
+  const int kBlockSize = 4;
+  const int kChunkSize = 5;
+  const int kShiftAmount = 2;
+
+  const float kInput[kNumInputChannels][kNumFrames] = {
+      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+      {2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
+      {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, 12, 12, 20, 20, 20, 20, 20, 20},
+      {6, 6, 12, 12, 28, 28, 28, 28, 28, 28}};
+  const ChannelBuffer<float> expected_output_cb(
+      kExpectedOutput[0], kNumFrames, kNumInputChannels);
+
+  const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
+
+  ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels);
+  ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels);
+  ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels);
+
+  SimpleBlockerCallback callback;
+  Blocker blocker(kChunkSize,
+                  kBlockSize,
+                  kNumInputChannels,
+                  kNumOutputChannels,
+                  kWindow,
+                  kShiftAmount,
+                  &callback);
+
+  RunTest(&blocker,
+          kChunkSize,
+          kNumFrames,
+          input_cb.channels(),
+          input_chunk_cb.channels(),
+          actual_output_cb.channels(),
+          output_chunk_cb.channels(),
+          kNumInputChannels,
+          kNumOutputChannels);
+
+  ValidateSignalEquality(expected_output_cb.channels(),
+                         actual_output_cb.channels(),
+                         kNumOutputChannels,
+                         kNumFrames);
+}
+
+TEST_F(BlockerTest, TestBlockerMutuallyPrimeShiftAndBlockSize) {
+  const int kNumInputChannels = 3;
+  const int kNumOutputChannels = 2;
+  const int kNumFrames = 12;
+  const int kBlockSize = 4;
+  const int kChunkSize = 6;
+  const int kShiftAmount = 3;
+
+  const float kInput[kNumInputChannels][kNumFrames] = {
+      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+      {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
+      {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 ChannelBuffer<float> expected_output_cb(
+      kExpectedOutput[0], kNumFrames, kNumInputChannels);
+
+  const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
+
+  ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels);
+  ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels);
+  ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels);
+
+  SimpleBlockerCallback callback;
+  Blocker blocker(kChunkSize,
+                  kBlockSize,
+                  kNumInputChannels,
+                  kNumOutputChannels,
+                  kWindow,
+                  kShiftAmount,
+                  &callback);
+
+  RunTest(&blocker,
+          kChunkSize,
+          kNumFrames,
+          input_cb.channels(),
+          input_chunk_cb.channels(),
+          actual_output_cb.channels(),
+          output_chunk_cb.channels(),
+          kNumInputChannels,
+          kNumOutputChannels);
+
+  ValidateSignalEquality(expected_output_cb.channels(),
+                         actual_output_cb.channels(),
+                         kNumOutputChannels,
+                         kNumFrames);
+}
+
+TEST_F(BlockerTest, TestBlockerNoOverlap) {
+  const int kNumInputChannels = 3;
+  const int kNumOutputChannels = 2;
+  const int kNumFrames = 12;
+  const int kBlockSize = 4;
+  const int kChunkSize = 4;
+  const int kShiftAmount = 4;
+
+  const float kInput[kNumInputChannels][kNumFrames] = {
+      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
+      {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2},
+      {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 ChannelBuffer<float> expected_output_cb(
+      kExpectedOutput[0], kNumFrames, kNumInputChannels);
+
+  const float kWindow[kBlockSize] = {2.f, 2.f, 2.f, 2.f};
+
+  ChannelBuffer<float> actual_output_cb(kNumFrames, kNumOutputChannels);
+  ChannelBuffer<float> input_chunk_cb(kChunkSize, kNumInputChannels);
+  ChannelBuffer<float> output_chunk_cb(kChunkSize, kNumOutputChannels);
+
+  SimpleBlockerCallback callback;
+  Blocker blocker(kChunkSize,
+                  kBlockSize,
+                  kNumInputChannels,
+                  kNumOutputChannels,
+                  kWindow,
+                  kShiftAmount,
+                  &callback);
+
+  RunTest(&blocker,
+          kChunkSize,
+          kNumFrames,
+          input_cb.channels(),
+          input_chunk_cb.channels(),
+          actual_output_cb.channels(),
+          output_chunk_cb.channels(),
+          kNumInputChannels,
+          kNumOutputChannels);
+
+  ValidateSignalEquality(expected_output_cb.channels(),
+                         actual_output_cb.channels(),
+                         kNumOutputChannels,
+                         kNumFrames);
+}
+
+}  // namespace webrtc
diff --git a/common_audio/common_audio.gyp b/common_audio/common_audio.gyp
index e9abf82..4581f58 100644
--- a/common_audio/common_audio.gyp
+++ b/common_audio/common_audio.gyp
@@ -30,6 +30,8 @@
       },
       'sources': [
         'audio_util.cc',
+        'blocker.cc',
+        'blocker.h',
         'fir_filter.cc',
         'fir_filter.h',
         'fir_filter_neon.h',
@@ -96,8 +98,23 @@
         'wav_header.h',
         'wav_writer.cc',
         'wav_writer.h',
+        'window_generator.cc',
+        'window_generator.h',
       ],
       'conditions': [
+        # TODO(ajm): Workaround until openmax_dl has non-Android ARM support.
+        # See: crbug.com/415393
+        ['target_arch!="arm" or (target_arch=="arm" and OS=="android")', {
+          'sources': [
+            'lapped_transform.cc',
+            'lapped_transform.h',
+            'real_fourier.cc',
+            'real_fourier.h',
+          ],
+          'dependencies': [
+            '<(DEPTH)/third_party/openmax_dl/dl/dl.gyp:openmax_dl',
+          ],
+        }],
         ['target_arch=="ia32" or target_arch=="x64"', {
           'dependencies': ['common_audio_sse2',],
         }],
@@ -209,6 +226,7 @@
           ],
           'sources': [
             'audio_util_unittest.cc',
+            'blocker_unittest.cc',
             'fir_filter_unittest.cc',
             'resampler/resampler_unittest.cc',
             'resampler/push_resampler_unittest.cc',
@@ -226,8 +244,17 @@
             'vad/vad_unittest.h',
             'wav_header_unittest.cc',
             'wav_writer_unittest.cc',
+            'window_generator_unittest.cc',
           ],
           'conditions': [
+            # TODO(ajm): Workaround until openmax_dl has non-Android ARM
+            # support. See: crbug.com/415393
+            ['target_arch!="arm" or (target_arch=="arm" and OS=="android")', {
+              'sources': [
+                'lapped_transform_unittest.cc',
+                'real_fourier_unittest.cc',
+              ],
+            }],
             ['OS=="android"', {
               'dependencies': [
                 '<(DEPTH)/testing/android/native_test.gyp:native_test_native_code',
diff --git a/common_audio/lapped_transform.cc b/common_audio/lapped_transform.cc
new file mode 100644
index 0000000..ed4610f
--- /dev/null
+++ b/common_audio/lapped_transform.cc
@@ -0,0 +1,107 @@
+/*
+ *  Copyright (c) 2014 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 "webrtc/common_audio/lapped_transform.h"
+
+#include <cstdlib>
+#include <cstring>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/common_audio/real_fourier.h"
+
+namespace webrtc {
+
+void LappedTransform::BlockThunk::ProcessBlock(const float* const* input,
+                                               int num_frames,
+                                               int num_input_channels,
+                                               int num_output_channels,
+                                               float* const* output) {
+  CHECK_EQ(num_input_channels, parent_->in_channels_);
+  CHECK_EQ(num_output_channels, parent_->out_channels_);
+  CHECK_EQ(parent_->block_length_, num_frames);
+
+  for (int i = 0; i < num_input_channels; ++i) {
+    memcpy(parent_->real_buf_.Row(i), input[i],
+           num_frames * sizeof(*input[0]));
+    parent_->fft_.Forward(parent_->real_buf_.Row(i), parent_->cplx_pre_.Row(i));
+  }
+
+  int block_length = RealFourier::ComplexLength(
+      RealFourier::FftOrder(num_frames));
+  CHECK_EQ(parent_->cplx_length_, block_length);
+  parent_->block_processor_->ProcessAudioBlock(parent_->cplx_pre_.Array(),
+                                               num_input_channels,
+                                               parent_->cplx_length_,
+                                               num_output_channels,
+                                               parent_->cplx_post_.Array());
+
+  for (int i = 0; i < num_output_channels; ++i) {
+    parent_->fft_.Inverse(parent_->cplx_post_.Row(i),
+                          parent_->real_buf_.Row(i));
+    memcpy(output[i], parent_->real_buf_.Row(i),
+           num_frames * sizeof(*input[0]));
+  }
+}
+
+LappedTransform::LappedTransform(int in_channels, int out_channels,
+                                 int chunk_length, const float* window,
+                                 int block_length, int shift_amount,
+                                 Callback* callback)
+    : blocker_callback_(this),
+      in_channels_(in_channels),
+      out_channels_(out_channels),
+      window_(window),
+      own_window_(false),
+      window_shift_amount_(shift_amount),
+      block_length_(block_length),
+      chunk_length_(chunk_length),
+      block_processor_(callback),
+      blocker_(nullptr),
+      fft_(RealFourier::FftOrder(block_length_)),
+      cplx_length_(RealFourier::ComplexLength(fft_.order())),
+      real_buf_(in_channels, block_length, RealFourier::kFftBufferAlignment),
+      cplx_pre_(in_channels, cplx_length_, RealFourier::kFftBufferAlignment),
+      cplx_post_(out_channels, cplx_length_, RealFourier::kFftBufferAlignment) {
+  CHECK(in_channels_ > 0 && out_channels_ > 0);
+  CHECK_GT(block_length_, 0);
+  CHECK_GT(chunk_length_, 0);
+  CHECK(block_processor_);
+  CHECK_EQ(0, block_length & (block_length - 1));  // block_length_ power of 2?
+
+  if (!window_) {
+    own_window_ = true;
+    window_ = new float[block_length_];
+    CHECK(window_ != nullptr);
+    window_shift_amount_ = block_length_;
+    float* temp = const_cast<float*>(window_);
+    for (int i = 0; i < block_length_; ++i) {
+      temp[i] = 1.0f;
+    }
+  }
+
+  blocker_.reset(new Blocker(chunk_length_, block_length_, in_channels_,
+                             out_channels_, window_, window_shift_amount_,
+                             &blocker_callback_));
+}
+
+LappedTransform::~LappedTransform() {
+  if (own_window_) {
+    delete [] window_;
+  }
+}
+
+void LappedTransform::ProcessChunk(const float* const* in_chunk,
+                                   float* const* out_chunk) {
+  blocker_->ProcessChunk(in_chunk, chunk_length_, in_channels_, out_channels_,
+                         out_chunk);
+}
+
+}  // namespace webrtc
+
diff --git a/common_audio/lapped_transform.h b/common_audio/lapped_transform.h
new file mode 100644
index 0000000..330886a
--- /dev/null
+++ b/common_audio/lapped_transform.h
@@ -0,0 +1,101 @@
+/*
+ *  Copyright (c) 2014 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.
+ */
+
+#ifndef WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_
+#define WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_
+
+#include <complex>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/common_audio/blocker.h"
+#include "webrtc/common_audio/real_fourier.h"
+#include "webrtc/system_wrappers/interface/aligned_array.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+// Helper class for audio processing modules which operate on frequency domain
+// input derived from the windowed time domain audio stream.
+//
+// The input audio chunk is sliced into possibly overlapping blocks, multiplied
+// by a window and transformed with an FFT implementation. The transformed data
+// is supplied to the given callback for processing. The processed output is
+// then inverse transformed into the time domain and spliced back into a chunk
+// which constitutes the final output of this processing module.
+class LappedTransform {
+ public:
+  class Callback {
+   public:
+    virtual ~Callback() {}
+
+    virtual void ProcessAudioBlock(const std::complex<float>* const* in_block,
+                                   int in_channels, int frames,
+                                   int out_channels,
+                                   std::complex<float>* const* out_block) = 0;
+  };
+
+  // Construct a transform instance. |chunk_length| is the number of samples in
+  // each channel. |window| defines the window, owned by the caller (a copy is
+  // made internally); can be NULL to disable windowing entirely.
+  // |block_length| defines the length of a block, in samples, even when
+  // windowing is disabled. |shift_length| is in samples. |callback| is the
+  // caller-owned audio processing function called for each block of the input
+  // chunk.
+  LappedTransform(int in_channels, int out_channels, int chunk_length,
+                  const float* window, int block_length, int shift_amount,
+                  Callback* callback);
+  ~LappedTransform();
+
+  // Main audio processing helper method. Internally slices |in_chunk| into
+  // blocks, transforms them to frequency domain, calls the callback for each
+  // block and returns a de-blocked time domain chunk of audio through
+  // |out_chunk|. Both buffers are caller-owned.
+  void ProcessChunk(const float* const* in_chunk, float* const* out_chunk);
+
+ private:
+  // Internal middleware callback, given to the blocker. Transforms each block
+  // and hands it over to the processing method given at construction time.
+  friend class BlockThunk;
+  class BlockThunk : public BlockerCallback {
+   public:
+    explicit BlockThunk(LappedTransform* parent) : parent_(parent) {}
+    virtual ~BlockThunk() {}
+
+    virtual void ProcessBlock(const float* const* input, int num_frames,
+                              int num_input_channels, int num_output_channels,
+                              float* const* output);
+
+   private:
+    LappedTransform* parent_;
+  } blocker_callback_;
+
+  int in_channels_;
+  int out_channels_;
+
+  const float* window_;
+  bool own_window_;
+  int window_shift_amount_;
+
+  int block_length_;
+  int chunk_length_;
+  Callback* block_processor_;
+  scoped_ptr<Blocker> blocker_;
+
+  RealFourier fft_;
+  int cplx_length_;
+  AlignedArray<float> real_buf_;
+  AlignedArray<std::complex<float> > cplx_pre_;
+  AlignedArray<std::complex<float> > cplx_post_;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_COMMON_AUDIO_LAPPED_TRANSFORM_H_
+
diff --git a/common_audio/lapped_transform_unittest.cc b/common_audio/lapped_transform_unittest.cc
new file mode 100644
index 0000000..9052382
--- /dev/null
+++ b/common_audio/lapped_transform_unittest.cc
@@ -0,0 +1,174 @@
+/*
+ *  Copyright (c) 2014 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 "webrtc/common_audio/lapped_transform.h"
+
+#include <cmath>
+#include <cstring>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::complex;
+
+namespace {
+
+class NoopCallback : public webrtc::LappedTransform::Callback {
+ public:
+  NoopCallback() : block_num_(0) {}
+
+  virtual void ProcessAudioBlock(const complex<float>* const* in_block,
+                                 int in_channels, int frames, int out_channels,
+                                 complex<float>* const* out_block) {
+    CHECK_EQ(in_channels, out_channels);
+    for (int i = 0; i < out_channels; ++i) {
+      memcpy(out_block[i], in_block[i], sizeof(**in_block) * frames);
+    }
+    ++block_num_;
+  }
+
+  int block_num() {
+    return block_num_;
+  }
+
+ private:
+  int block_num_;
+};
+
+class FftCheckerCallback : public webrtc::LappedTransform::Callback {
+ public:
+  FftCheckerCallback() : block_num_(0) {}
+
+  virtual void ProcessAudioBlock(const complex<float>* const* in_block,
+                                 int in_channels, int frames, int out_channels,
+                                 complex<float>* const* out_block) {
+    CHECK_EQ(in_channels, out_channels);
+
+    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 {
+      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) {
+        ASSERT_NEAR(in_block[0][i].real(), 0.0f, 1e-5f);
+        ASSERT_NEAR(in_block[0][i].imag(), 0.0f, 1e-5f);
+      }
+    }
+  }
+
+  int block_num() {
+    return block_num_;
+  }
+
+ private:
+  int block_num_;
+};
+
+void SetFloatArray(float value, int rows, int cols, float* const* array) {
+  for (int i = 0; i < rows; ++i) {
+    for (int j = 0; j < cols; ++j) {
+      array[i][j] = value;
+    }
+  }
+}
+
+}  // namespace
+
+namespace webrtc {
+
+TEST(LappedTransformTest, Windowless) {
+  const int kChannels = 3;
+  const int kChunkLength = 512;
+  const int kBlockLength = 64;
+  const int kShiftAmount = 32;
+  NoopCallback noop;
+  LappedTransform trans(kChannels, kChannels, kChunkLength, nullptr,
+                        kBlockLength, kShiftAmount, &noop);
+  float in_buffer[kChannels][kChunkLength];
+  float* in_chunk[kChannels];
+  float out_buffer[kChannels][kChunkLength];
+  float* out_chunk[kChannels];
+
+  in_chunk[0] = in_buffer[0];
+  in_chunk[1] = in_buffer[1];
+  in_chunk[2] = in_buffer[2];
+  out_chunk[0] = out_buffer[0];
+  out_chunk[1] = out_buffer[1];
+  out_chunk[2] = out_buffer[2];
+  SetFloatArray(2.0f, kChannels, kChunkLength, in_chunk);
+  SetFloatArray(-1.0f, kChannels, kChunkLength, out_chunk);
+
+  trans.ProcessChunk(in_chunk, out_chunk);
+
+  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_EQ(kChunkLength / kBlockLength, noop.block_num());
+}
+
+TEST(LappedTransformTest, IdentityProcessor) {
+  const int kChunkLength = 512;
+  const int kBlockLength = 64;
+  const int kShiftAmount = 32;
+  NoopCallback noop;
+  float window[kBlockLength];
+  float* window_ptr = window;
+
+  // Identity window for |overlap = block_size / 2|.
+  SetFloatArray(sqrtf(0.5f), 1, kBlockLength, &window_ptr);
+
+  LappedTransform trans(1, 1, kChunkLength, window, kBlockLength, kShiftAmount,
+                        &noop);
+  float in_buffer[kChunkLength];
+  float* in_chunk = in_buffer;
+  float out_buffer[kChunkLength];
+  float* out_chunk = out_buffer;
+
+  SetFloatArray(2.0f, 1, kChunkLength, &in_chunk);
+  SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk);
+
+  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_EQ(kChunkLength / kShiftAmount, noop.block_num());
+}
+
+TEST(LappedTransformTest, Callbacks) {
+  const int kChunkLength = 512;
+  const int kBlockLength = 64;
+  FftCheckerCallback call;
+  LappedTransform trans(1, 1, kChunkLength, nullptr, kBlockLength,
+                        kBlockLength, &call);
+  float in_buffer[kChunkLength];
+  float* in_chunk = in_buffer;
+  float out_buffer[kChunkLength];
+  float* out_chunk = out_buffer;
+
+  SetFloatArray(1.0f, 1, kChunkLength, &in_chunk);
+  SetFloatArray(-1.0f, 1, kChunkLength, &out_chunk);
+
+  trans.ProcessChunk(&in_chunk, &out_chunk);
+
+  ASSERT_EQ(kChunkLength / kBlockLength, call.block_num());
+}
+
+}  // namespace webrtc
+
diff --git a/common_audio/real_fourier.cc b/common_audio/real_fourier.cc
new file mode 100644
index 0000000..6933ea7
--- /dev/null
+++ b/common_audio/real_fourier.cc
@@ -0,0 +1,88 @@
+/*
+ *  Copyright (c) 2014 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 "webrtc/common_audio/real_fourier.h"
+
+#include <cstdlib>
+
+#include "third_party/openmax_dl/dl/sp/api/omxSP.h"
+#include "webrtc/base/checks.h"
+
+namespace webrtc {
+
+using std::complex;
+
+// The omx implementation uses this macro to check order validity.
+const int RealFourier::kMaxFftOrder = TWIDDLE_TABLE_ORDER;
+const int RealFourier::kFftBufferAlignment = 32;
+
+RealFourier::RealFourier(int fft_order)
+    : order_(fft_order),
+      omx_spec_(nullptr) {
+  CHECK_GE(order_, 1);
+  CHECK_LE(order_, kMaxFftOrder);
+
+  OMX_INT buffer_size;
+  OMXResult r;
+
+  r = omxSP_FFTGetBufSize_R_F32(order_, &buffer_size);
+  CHECK_EQ(r, OMX_Sts_NoErr);
+
+  omx_spec_ = malloc(buffer_size);
+  DCHECK(omx_spec_);
+
+  r = omxSP_FFTInit_R_F32(omx_spec_, order_);
+  CHECK_EQ(r, OMX_Sts_NoErr);
+}
+
+RealFourier::~RealFourier() {
+  free(omx_spec_);
+}
+
+int RealFourier::FftOrder(int length) {
+  for (int order = 0; order <= kMaxFftOrder; order++) {
+    if ((1 << order) >= length) {
+      return order;
+    }
+  }
+  return -1;
+}
+
+int RealFourier::ComplexLength(int order) {
+  CHECK_LE(order, kMaxFftOrder);
+  CHECK_GT(order, 0);
+  return (1 << order) / 2 + 1;
+}
+
+RealFourier::fft_real_scoper RealFourier::AllocRealBuffer(int count) {
+  return fft_real_scoper(static_cast<float*>(
+      AlignedMalloc(sizeof(float) * count, kFftBufferAlignment)));
+}
+
+RealFourier::fft_cplx_scoper RealFourier::AllocCplxBuffer(int count) {
+  return fft_cplx_scoper(static_cast<complex<float>*>(
+      AlignedMalloc(sizeof(complex<float>) * count, kFftBufferAlignment)));
+}
+
+void RealFourier::Forward(const float* src, complex<float>* dest) const {
+  OMXResult r;
+  r = omxSP_FFTFwd_RToCCS_F32(src, reinterpret_cast<OMX_F32*>(dest), omx_spec_);
+  CHECK_EQ(r, OMX_Sts_NoErr);
+}
+
+void RealFourier::Inverse(const complex<float>* src, float* dest) const {
+  OMXResult r;
+  r = omxSP_FFTInv_CCSToR_F32(reinterpret_cast<const OMX_F32*>(src), dest,
+                              omx_spec_);
+  CHECK_EQ(r, OMX_Sts_NoErr);
+}
+
+}  // namespace webrtc
+
diff --git a/common_audio/real_fourier.h b/common_audio/real_fourier.h
new file mode 100644
index 0000000..0ca962c
--- /dev/null
+++ b/common_audio/real_fourier.h
@@ -0,0 +1,85 @@
+/*
+ *  Copyright (c) 2014 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.
+ */
+
+#ifndef WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_
+#define WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_
+
+#include <complex>
+
+#include "webrtc/system_wrappers/interface/aligned_malloc.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+// Uniform interface class for the real DFT and its inverse, for power-of-2
+// input lengths. Also contains helper functions for buffer allocation, taking
+// care of any memory alignment requirements the underlying library might have.
+
+namespace webrtc {
+
+class RealFourier {
+ public:
+  // Shorthand typenames for the scopers used by the buffer allocation helpers.
+  typedef scoped_ptr<float[], AlignedFreeDeleter> fft_real_scoper;
+  typedef scoped_ptr<std::complex<float>[], AlignedFreeDeleter> fft_cplx_scoper;
+
+  // The maximum input order supported by this implementation.
+  static const int kMaxFftOrder;
+
+  // The alignment required for all input and output buffers, in bytes.
+  static const int kFftBufferAlignment;
+
+  // Construct a wrapper instance for the given input order, which must be
+  // between 1 and kMaxFftOrder, inclusively.
+  explicit RealFourier(int fft_order);
+  ~RealFourier();
+
+  // Short helper to compute the smallest FFT order (a power of 2) which will
+  // contain the given input length. Returns -1 if the order would have been
+  // too big for the implementation.
+  static int FftOrder(int length);
+
+  // Short helper to compute the exact length, in complex floats, of the
+  // transform output (i.e. |2^order / 2 + 1|).
+  static int ComplexLength(int order);
+
+  // Buffer allocation helpers. The buffers are large enough to hold |count|
+  // floats/complexes and suitably aligned for use by the implementation.
+  // The returned scopers are set up with proper deleters; the caller owns
+  // the allocated memory.
+  static fft_real_scoper AllocRealBuffer(int count);
+  static fft_cplx_scoper AllocCplxBuffer(int count);
+
+  // Main forward transform interface. The output array need only be big
+  // enough for |2^order / 2 + 1| elements - the conjugate pairs are not
+  // returned. Input and output must be properly aligned (e.g. through
+  // AllocRealBuffer and AllocCplxBuffer) and input length must be
+  // |2^order| (same as given at construction time).
+  void Forward(const float* src, std::complex<float>* dest) const;
+
+  // Inverse transform. Same input format as output above, conjugate pairs
+  // not needed.
+  void Inverse(const std::complex<float>* src, float* dest) const;
+
+  int order() const {
+    return order_;
+  }
+
+ private:
+  // Basically a forward declare of OMXFFTSpec_R_F32. To get rid of the
+  // dependency on openmax.
+  typedef void OMXFFTSpec_R_F32_;
+  const int order_;
+
+  OMXFFTSpec_R_F32_* omx_spec_;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_COMMON_AUDIO_REAL_FOURIER_H_
+
diff --git a/common_audio/real_fourier_unittest.cc b/common_audio/real_fourier_unittest.cc
new file mode 100644
index 0000000..8660d4d
--- /dev/null
+++ b/common_audio/real_fourier_unittest.cc
@@ -0,0 +1,105 @@
+/*
+ *  Copyright (c) 2014 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 "webrtc/common_audio/real_fourier.h"
+
+#include <stdlib.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+
+using std::complex;
+
+TEST(RealFourierStaticsTest, AllocatorAlignment) {
+  {
+    RealFourier::fft_real_scoper real;
+    real = RealFourier::AllocRealBuffer(3);
+    ASSERT_TRUE(real.get() != nullptr);
+    int64_t ptr_value = reinterpret_cast<int64_t>(real.get());
+    ASSERT_EQ(ptr_value % RealFourier::kFftBufferAlignment, 0);
+  }
+  {
+    RealFourier::fft_cplx_scoper cplx;
+    cplx = RealFourier::AllocCplxBuffer(3);
+    ASSERT_TRUE(cplx.get() != nullptr);
+    int64_t ptr_value = reinterpret_cast<int64_t>(cplx.get());
+    ASSERT_EQ(ptr_value % RealFourier::kFftBufferAlignment, 0);
+  }
+}
+
+TEST(RealFourierStaticsTest, OrderComputation) {
+  ASSERT_EQ(RealFourier::FftOrder(2000000), -1);
+  ASSERT_EQ(RealFourier::FftOrder((1 << RealFourier::kMaxFftOrder) + 1), -1);
+  ASSERT_EQ(RealFourier::FftOrder(1 << RealFourier::kMaxFftOrder),
+            RealFourier::kMaxFftOrder);
+  ASSERT_EQ(RealFourier::FftOrder(13), 4);
+  ASSERT_EQ(RealFourier::FftOrder(32), 5);
+  ASSERT_EQ(RealFourier::FftOrder(2), 1);
+  ASSERT_EQ(RealFourier::FftOrder(1), 0);
+  ASSERT_EQ(RealFourier::FftOrder(0), 0);
+}
+
+TEST(RealFourierStaticsTest, ComplexLengthComputation) {
+  ASSERT_EQ(RealFourier::ComplexLength(1), 2);
+  ASSERT_EQ(RealFourier::ComplexLength(2), 3);
+  ASSERT_EQ(RealFourier::ComplexLength(3), 5);
+  ASSERT_EQ(RealFourier::ComplexLength(4), 9);
+  ASSERT_EQ(RealFourier::ComplexLength(5), 17);
+  ASSERT_EQ(RealFourier::ComplexLength(7), 65);
+}
+
+class RealFourierTest : public ::testing::Test {
+ protected:
+  RealFourierTest()
+      : rf_(new RealFourier(2)),
+        real_buffer_(RealFourier::AllocRealBuffer(4)),
+        cplx_buffer_(RealFourier::AllocCplxBuffer(3)) {}
+
+  ~RealFourierTest() {
+    delete rf_;
+  }
+
+  const RealFourier* rf_;
+  const RealFourier::fft_real_scoper real_buffer_;
+  const RealFourier::fft_cplx_scoper cplx_buffer_;
+};
+
+TEST_F(RealFourierTest, SimpleForwardTransform) {
+  real_buffer_[0] = 1.0f;
+  real_buffer_[1] = 2.0f;
+  real_buffer_[2] = 3.0f;
+  real_buffer_[3] = 4.0f;
+
+  rf_->Forward(real_buffer_.get(), cplx_buffer_.get());
+
+  ASSERT_NEAR(cplx_buffer_[0].real(), 10.0f, 1e-8f);
+  ASSERT_NEAR(cplx_buffer_[0].imag(), 0.0f, 1e-8f);
+  ASSERT_NEAR(cplx_buffer_[1].real(), -2.0f, 1e-8f);
+  ASSERT_NEAR(cplx_buffer_[1].imag(), 2.0f, 1e-8f);
+  ASSERT_NEAR(cplx_buffer_[2].real(), -2.0f, 1e-8f);
+  ASSERT_NEAR(cplx_buffer_[2].imag(), 0.0f, 1e-8f);
+}
+
+TEST_F(RealFourierTest, SimpleBackwardTransform) {
+  cplx_buffer_[0] = complex<float>(10.0f, 0.0f);
+  cplx_buffer_[1] = complex<float>(-2.0f, 2.0f);
+  cplx_buffer_[2] = complex<float>(-2.0f, 0.0f);
+
+  rf_->Inverse(cplx_buffer_.get(), real_buffer_.get());
+
+  ASSERT_NEAR(real_buffer_[0], 1.0f, 1e-8f);
+  ASSERT_NEAR(real_buffer_[1], 2.0f, 1e-8f);
+  ASSERT_NEAR(real_buffer_[2], 3.0f, 1e-8f);
+  ASSERT_NEAR(real_buffer_[3], 4.0f, 1e-8f);
+}
+
+}  // namespace webrtc
+
diff --git a/common_audio/window_generator.cc b/common_audio/window_generator.cc
new file mode 100644
index 0000000..1d61368
--- /dev/null
+++ b/common_audio/window_generator.cc
@@ -0,0 +1,72 @@
+/*
+ *  Copyright (c) 2014 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.
+ */
+
+#define _USE_MATH_DEFINES
+
+#include "webrtc/common_audio/window_generator.h"
+
+#include <cmath>
+#include <complex>
+
+#include "webrtc/base/checks.h"
+
+using std::complex;
+
+namespace {
+
+// Modified Bessel function of order 0 for complex inputs.
+complex<float> I0(complex<float> x) {
+  complex<float> y = x / 3.75f;
+  y *= y;
+  return 1.0f + y * (
+    3.5156229f + y * (
+      3.0899424f + y * (
+        1.2067492f + y * (
+          0.2659732f + y * (
+            0.360768e-1f + y * 0.45813e-2f)))));
+}
+
+}  // namespace
+
+namespace webrtc {
+
+void WindowGenerator::Hanning(int length, float* window) {
+  CHECK_GT(length, 1);
+  CHECK(window != nullptr);
+  for (int i = 0; i < length; ++i) {
+    window[i] = 0.5f * (1 - cosf(2 * static_cast<float>(M_PI) * i /
+                                 (length - 1)));
+  }
+}
+
+void WindowGenerator::KaiserBesselDerived(float alpha, int length,
+                                          float* window) {
+  CHECK_GT(length, 1);
+  CHECK(window != nullptr);
+
+  const int half = (length + 1) / 2;
+  float sum = 0.0f;
+
+  for (int i = 0; i <= half; ++i) {
+    complex<float> r = (4.0f * i) / length - 1.0f;
+    sum += I0(static_cast<float>(M_PI) * alpha * sqrt(1.0f - r * r)).real();
+    window[i] = sum;
+  }
+  for (int i = length - 1; i >= half; --i) {
+    window[length - i - 1] = sqrtf(window[length - i - 1] / sum);
+    window[i] = window[length - i - 1];
+  }
+  if (length % 2 == 1) {
+    window[half - 1] = sqrtf(window[half - 1] / sum);
+  }
+}
+
+}  // namespace webrtc
+
diff --git a/common_audio/window_generator.h b/common_audio/window_generator.h
new file mode 100644
index 0000000..ee0acad
--- /dev/null
+++ b/common_audio/window_generator.h
@@ -0,0 +1,31 @@
+/*
+ *  Copyright (c) 2014 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.
+ */
+
+#ifndef WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_
+#define WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_
+
+#include "webrtc/base/constructormagic.h"
+
+namespace webrtc {
+
+// Helper class with generators for various signal transform windows.
+class WindowGenerator {
+ public:
+  static void Hanning(int length, float* window);
+  static void KaiserBesselDerived(float alpha, int length, float* window);
+
+ private:
+  DISALLOW_IMPLICIT_CONSTRUCTORS(WindowGenerator);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_COMMON_AUDIO_WINDOW_GENERATOR_H_
+
diff --git a/common_audio/window_generator_unittest.cc b/common_audio/window_generator_unittest.cc
new file mode 100644
index 0000000..124b301
--- /dev/null
+++ b/common_audio/window_generator_unittest.cc
@@ -0,0 +1,92 @@
+/*
+ *  Copyright (c) 2014 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 "webrtc/common_audio/window_generator.h"
+
+#include <cstring>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace webrtc {
+
+TEST(WindowGeneratorTest, KaiserBesselDerived) {
+  float window[7];
+
+  memset(window, 0, sizeof(window));
+
+  WindowGenerator::KaiserBesselDerived(0.397856f, 2, window);
+  ASSERT_NEAR(window[0], 0.707106f, 1e-6f);
+  ASSERT_NEAR(window[1], 0.707106f, 1e-6f);
+  ASSERT_NEAR(window[2], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[3], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[4], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[5], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[6], 0.0f, 1e-6f);
+
+  WindowGenerator::KaiserBesselDerived(0.397856f, 3, window);
+  ASSERT_NEAR(window[0], 0.598066f, 1e-6f);
+  ASSERT_NEAR(window[1], 0.922358f, 1e-6f);
+  ASSERT_NEAR(window[2], 0.598066f, 1e-6f);
+  ASSERT_NEAR(window[3], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[4], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[5], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[6], 0.0f, 1e-6f);
+
+  WindowGenerator::KaiserBesselDerived(0.397856f, 6, window);
+  ASSERT_NEAR(window[0], 0.458495038865344f, 1e-6f);
+  ASSERT_NEAR(window[1], 0.707106781186548f, 1e-6f);
+  ASSERT_NEAR(window[2], 0.888696967101760f, 1e-6f);
+  ASSERT_NEAR(window[3], 0.888696967101760f, 1e-6f);
+  ASSERT_NEAR(window[4], 0.707106781186548f, 1e-6f);
+  ASSERT_NEAR(window[5], 0.458495038865344f, 1e-6f);
+  ASSERT_NEAR(window[6], 0.0f, 1e-6f);
+}
+
+TEST(WindowGeneratorTest, Hanning) {
+  float window[7];
+
+  memset(window, 0, sizeof(window));
+
+  window[0] = -1.0f;
+  window[1] = -1.0f;
+  WindowGenerator::Hanning(2, window);
+  ASSERT_NEAR(window[0], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[1], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[2], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[3], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[4], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[5], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[6], 0.0f, 1e-6f);
+
+  window[0] = -1.0f;
+  window[2] = -1.0f;
+  WindowGenerator::Hanning(3, window);
+  ASSERT_NEAR(window[0], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[1], 1.0f, 1e-6f);
+  ASSERT_NEAR(window[2], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[3], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[4], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[5], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[6], 0.0f, 1e-6f);
+
+  window[0] = -1.0f;
+  window[5] = -1.0f;
+  WindowGenerator::Hanning(6, window);
+  ASSERT_NEAR(window[0], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[1], 0.345491f, 1e-6f);
+  ASSERT_NEAR(window[2], 0.904508f, 1e-6f);
+  ASSERT_NEAR(window[3], 0.904508f, 1e-6f);
+  ASSERT_NEAR(window[4], 0.345491f, 1e-6f);
+  ASSERT_NEAR(window[5], 0.0f, 1e-6f);
+  ASSERT_NEAR(window[6], 0.0f, 1e-6f);
+}
+
+}  // namespace webrtc
+
diff --git a/system_wrappers/BUILD.gn b/system_wrappers/BUILD.gn
index 14deab9..c193c28 100644
--- a/system_wrappers/BUILD.gn
+++ b/system_wrappers/BUILD.gn
@@ -17,6 +17,7 @@
 
 static_library("system_wrappers") {
   sources = [
+    "interface/aligned_array.h",
     "interface/aligned_malloc.h",
     "interface/atomic32.h",
     "interface/clock.h",
@@ -107,7 +108,7 @@
 
   configs += [ "..:common_config" ]
 
- if (is_clang) {
+  if (is_clang) {
     # Suppress warnings from Chrome's Clang plugins.
     # See http://code.google.com/p/webrtc/issues/detail?id=163 for details.
     configs -= [ "//build/config/clang:find_bad_constructs" ]
diff --git a/system_wrappers/interface/aligned_array.h b/system_wrappers/interface/aligned_array.h
new file mode 100644
index 0000000..4b5c276
--- /dev/null
+++ b/system_wrappers/interface/aligned_array.h
@@ -0,0 +1,89 @@
+/*
+ *  Copyright (c) 2014 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.
+ */
+
+#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_ALIGNED_ARRAY_
+#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_ALIGNED_ARRAY_
+
+#include "webrtc/base/checks.h"
+#include "webrtc/system_wrappers/interface/aligned_malloc.h"
+
+namespace webrtc {
+
+// Wrapper class for aligned arrays. Every row (and the first dimension) are
+// aligned to the given byte alignment.
+template<typename T> class AlignedArray {
+ public:
+  AlignedArray(int rows, int cols, int alignment)
+      : rows_(rows),
+        cols_(cols),
+        alignment_(alignment) {
+    CHECK_GT(alignment_, 0);
+    head_row_ = static_cast<T**>(AlignedMalloc(rows_ * sizeof(*head_row_),
+                                               alignment_));
+    for (int i = 0; i < rows_; ++i) {
+      head_row_[i] = static_cast<T*>(AlignedMalloc(cols_ * sizeof(**head_row_),
+                                                   alignment_));
+    }
+  }
+
+  ~AlignedArray() {
+    for (int i = 0; i < rows_; ++i) {
+      AlignedFree(head_row_[i]);
+    }
+    AlignedFree(head_row_);
+  }
+
+  T* const* Array() {
+    return head_row_;
+  }
+
+  const T* const* Array() const {
+    return head_row_;
+  }
+
+  T* Row(int row) {
+    CHECK_LE(row, rows_);
+    return head_row_[row];
+  }
+
+  const T* Row(int row) const {
+    CHECK_LE(row, rows_);
+    return head_row_[row];
+  }
+
+  T& At(int row, int col) {
+    CHECK_LE(col, cols_);
+    return Row(row)[col];
+  }
+
+  const T& At(int row, int col) const {
+    CHECK_LE(col, cols_);
+    return Row(row)[col];
+  }
+
+  int rows() const {
+    return rows_;
+  }
+
+  int cols() const {
+    return cols_;
+  }
+
+ private:
+  int rows_;
+  int cols_;
+  int alignment_;
+  T** head_row_;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_ALIGNED_ARRAY_
+
diff --git a/system_wrappers/source/aligned_array_unittest.cc b/system_wrappers/source/aligned_array_unittest.cc
new file mode 100644
index 0000000..e5e556d
--- /dev/null
+++ b/system_wrappers/source/aligned_array_unittest.cc
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2014 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 "webrtc/system_wrappers/interface/aligned_array.h"
+
+#include <stdint.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+bool IsAligned(const void* ptr, int alignment) {
+  return reinterpret_cast<uintptr_t>(ptr) % alignment == 0;
+}
+
+}  // namespace
+
+namespace webrtc {
+
+TEST(AlignedArrayTest, CheckAlignment) {
+  AlignedArray<bool> arr(10, 7, 128);
+  ASSERT_TRUE(IsAligned(arr.Array(), 128));
+  for (int i = 0; i < 10; ++i) {
+    ASSERT_TRUE(IsAligned(arr.Row(i), 128));
+    ASSERT_EQ(arr.Row(i), arr.Array()[i]);
+  }
+}
+
+TEST(AlignedArrayTest, CheckOverlap) {
+  AlignedArray<int> arr(10, 7, 128);
+
+  for (int i = 0; i < 10; ++i) {
+    for (int j = 0; j < 7; ++j) {
+      arr.At(i, j) = 20 * i + j;
+    }
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    for (int j = 0; j < 7; ++j) {
+      ASSERT_EQ(arr.At(i, j), 20 * i + j);
+      ASSERT_EQ(arr.Row(i)[j], 20 * i + j);
+      ASSERT_EQ(arr.Array()[i][j], 20 * i + j);
+    }
+  }
+}
+
+}  // namespace webrtc
+
diff --git a/system_wrappers/source/system_wrappers.gyp b/system_wrappers/source/system_wrappers.gyp
index e6c4216..870d88a 100644
--- a/system_wrappers/source/system_wrappers.gyp
+++ b/system_wrappers/source/system_wrappers.gyp
@@ -25,6 +25,7 @@
         ],
       },
       'sources': [
+        '../interface/aligned_array.h',
         '../interface/aligned_malloc.h',
         '../interface/atomic32.h',
         '../interface/clock.h',
diff --git a/system_wrappers/source/system_wrappers_tests.gyp b/system_wrappers/source/system_wrappers_tests.gyp
index 75081b9..f77b985 100644
--- a/system_wrappers/source/system_wrappers_tests.gyp
+++ b/system_wrappers/source/system_wrappers_tests.gyp
@@ -18,6 +18,7 @@
         '<(webrtc_root)/test/test.gyp:test_support_main',
       ],
       'sources': [
+        'aligned_array_unittest.cc',
         'aligned_malloc_unittest.cc',
         'clock_unittest.cc',
         'condition_variable_unittest.cc',