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@7359 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
andrew@webrtc.org 2014-10-01 17:42:18 +00:00
parent 593c3a0868
commit 325cff01b4
19 changed files with 1634 additions and 1 deletions

View File

@ -20,6 +20,8 @@ config("common_audio_config") {
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 @@ source_set("common_audio") {
"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",

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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',

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -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

View File

@ -17,6 +17,7 @@ config("system_wrappers_inherited_config") {
static_library("system_wrappers") {
sources = [
"interface/aligned_array.h",
"interface/aligned_malloc.h",
"interface/atomic32.h",
"interface/clock.h",
@ -107,7 +108,7 @@ static_library("system_wrappers") {
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" ]

View File

@ -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_

View File

@ -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

View File

@ -25,6 +25,7 @@
],
},
'sources': [
'../interface/aligned_array.h',
'../interface/aligned_malloc.h',
'../interface/atomic32.h',
'../interface/clock.h',

View File

@ -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',