Add support for arbitrary array geometries in Beamformer

R=andrew@webrtc.org, mgraczyk@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#8621}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8621 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
aluebs@webrtc.org 2015-03-05 20:38:21 +00:00
parent 0933d01d09
commit 1d88394bcb
8 changed files with 234 additions and 316 deletions

View File

@ -0,0 +1,49 @@
/*
* Copyright (c) 2015 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_MODULES_AUDIO_PROCESSING_BEAMFORMER_ARRAY_UTIL_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_ARRAY_UTIL_H_
#include <cmath>
namespace webrtc {
// Coordinates in meters.
template<typename T>
struct CartesianPoint {
CartesianPoint(T x, T y, T z) {
c[0] = x;
c[1] = y;
c[2] = z;
}
T x() const {
return c[0];
}
T y() const {
return c[1];
}
T z() const {
return c[2];
}
T c[3];
};
typedef CartesianPoint<float> Point;
template<typename T>
float Distance(CartesianPoint<T> a, CartesianPoint<T> b) {
return std::sqrt((a.x() - b.x()) * (a.x() - b.x()) +
(a.y() - b.y()) * (a.y() - b.y()) +
(a.z() - b.z()) * (a.z() - b.z()));
}
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_ARRAY_UTIL_H_

View File

@ -30,15 +30,16 @@ const float kMaskMinimum = 0.01f;
const float kSpeedOfSoundMeterSeconds = 343;
// For both target and interference angles, 0 is perpendicular to the microphone
// array, facing forwards. The positive direction goes counterclockwise.
// For both target and interference angles, PI / 2 is perpendicular to the
// microphone array, facing forwards. The positive direction goes
// counterclockwise.
// The angle at which we amplify sound.
const float kTargetAngleRadians = 0.f;
const float kTargetAngleRadians = static_cast<float>(M_PI) / 2.f;
// The angle at which we suppress sound. Suppression is symmetric around 0
// The angle at which we suppress sound. Suppression is symmetric around PI / 2
// radians, so sound is suppressed at both +|kInterfAngleRadians| and
// -|kInterfAngleRadians|. Since the beamformer is robust, this should
// suppress sound coming from angles near +-|kInterfAngleRadians| as well.
// PI - |kInterfAngleRadians|. Since the beamformer is robust, this should
// suppress sound coming from close angles as well.
const float kInterfAngleRadians = static_cast<float>(M_PI) / 4.f;
// When calculating the interference covariance matrix, this is the weight for
@ -50,15 +51,6 @@ const float kBalance = 0.4f;
// TODO(claguna): need comment here.
const float kBeamwidthConstant = 0.00002f;
// Width of the boxcar.
const float kBoxcarHalfWidth = 0.01f;
// We put a gap in the covariance matrix where we expect the target to come
// from. Warning: This must be very small, ex. < 0.01, because otherwise it can
// cause the covariance matrix not to be positive semidefinite, and we require
// that our covariance matrices are positive semidefinite.
const float kCovUniformGapHalfWidth = 0.01f;
// Alpha coefficient for mask smoothing.
const float kMaskSmoothAlpha = 0.2f;
@ -151,11 +143,40 @@ float SumSquares(const ComplexMatrix<float>& mat) {
return sum_squares;
}
// Does |out| = |in|.' * conj(|in|) for row vector |in|.
void TransposedConjugatedProduct(const ComplexMatrix<float>& in,
ComplexMatrix<float>* out) {
CHECK_EQ(in.num_rows(), 1);
CHECK_EQ(out->num_rows(), in.num_columns());
CHECK_EQ(out->num_columns(), in.num_columns());
const complex<float>* in_elements = in.elements()[0];
complex<float>* const* out_elements = out->elements();
for (int i = 0; i < out->num_rows(); ++i) {
for (int j = 0; j < out->num_columns(); ++j) {
out_elements[i][j] = in_elements[i] * conj(in_elements[j]);
}
}
}
std::vector<Point> GetCenteredArray(std::vector<Point> array_geometry) {
for (int dim = 0; dim < 3; ++dim) {
float center = 0.f;
for (size_t i = 0; i < array_geometry.size(); ++i) {
center += array_geometry[i].c[dim];
}
center /= array_geometry.size();
for (size_t i = 0; i < array_geometry.size(); ++i) {
array_geometry[i].c[dim] -= center;
}
}
return array_geometry;
}
} // namespace
Beamformer::Beamformer(const std::vector<Point>& array_geometry)
: num_input_channels_(array_geometry.size()),
mic_spacing_(MicSpacingFromGeometry(array_geometry)) {
array_geometry_(GetCenteredArray(array_geometry)) {
WindowGenerator::KaiserBesselDerived(kAlpha, kFftSize, window_);
}
@ -210,16 +231,14 @@ void Beamformer::Initialize(int chunk_size_ms, int sample_rate_hz) {
}
void Beamformer::InitDelaySumMasks() {
float sin_target = sin(kTargetAngleRadians);
for (int f_ix = 0; f_ix < kNumFreqBins; ++f_ix) {
delay_sum_masks_[f_ix].Resize(1, num_input_channels_);
CovarianceMatrixGenerator::PhaseAlignmentMasks(f_ix,
kFftSize,
sample_rate_hz_,
kSpeedOfSoundMeterSeconds,
mic_spacing_,
num_input_channels_,
sin_target,
array_geometry_,
kTargetAngleRadians,
&delay_sum_masks_[f_ix]);
complex_f norm_factor = sqrt(
@ -232,45 +251,23 @@ void Beamformer::InitDelaySumMasks() {
}
void Beamformer::InitTargetCovMats() {
target_cov_mats_[0].Resize(num_input_channels_, num_input_channels_);
CovarianceMatrixGenerator::DCCovarianceMatrix(
num_input_channels_, kBoxcarHalfWidth, &target_cov_mats_[0]);
complex_f normalization_factor = target_cov_mats_[0].Trace();
target_cov_mats_[0].Scale(1.f / normalization_factor);
for (int i = 1; i < kNumFreqBins; ++i) {
for (int i = 0; i < kNumFreqBins; ++i) {
target_cov_mats_[i].Resize(num_input_channels_, num_input_channels_);
CovarianceMatrixGenerator::Boxcar(wave_numbers_[i],
num_input_channels_,
mic_spacing_,
kBoxcarHalfWidth,
&target_cov_mats_[i]);
TransposedConjugatedProduct(delay_sum_masks_[i], &target_cov_mats_[i]);
complex_f normalization_factor = target_cov_mats_[i].Trace();
target_cov_mats_[i].Scale(1.f / normalization_factor);
}
}
void Beamformer::InitInterfCovMats() {
interf_cov_mats_[0].Resize(num_input_channels_, num_input_channels_);
CovarianceMatrixGenerator::DCCovarianceMatrix(
num_input_channels_, kCovUniformGapHalfWidth, &interf_cov_mats_[0]);
complex_f normalization_factor = interf_cov_mats_[0].Trace();
interf_cov_mats_[0].Scale(1.f / normalization_factor);
reflected_interf_cov_mats_[0].PointwiseConjugate(interf_cov_mats_[0]);
for (int i = 1; i < kNumFreqBins; ++i) {
for (int i = 0; i < kNumFreqBins; ++i) {
interf_cov_mats_[i].Resize(num_input_channels_, num_input_channels_);
ComplexMatrixF uniform_cov_mat(num_input_channels_, num_input_channels_);
ComplexMatrixF angled_cov_mat(num_input_channels_, num_input_channels_);
CovarianceMatrixGenerator::GappedUniformCovarianceMatrix(
wave_numbers_[i],
num_input_channels_,
mic_spacing_,
kCovUniformGapHalfWidth,
&uniform_cov_mat);
CovarianceMatrixGenerator::UniformCovarianceMatrix(wave_numbers_[i],
array_geometry_,
&uniform_cov_mat);
CovarianceMatrixGenerator::AngledCovarianceMatrix(kSpeedOfSoundMeterSeconds,
kInterfAngleRadians,
@ -278,8 +275,7 @@ void Beamformer::InitInterfCovMats() {
kFftSize,
kNumFreqBins,
sample_rate_hz_,
num_input_channels_,
mic_spacing_,
array_geometry_,
&angled_cov_mat);
// Normalize matrices before averaging them.
complex_f normalization_factor = uniform_cov_mat.Trace();
@ -447,20 +443,6 @@ void Beamformer::ApplyHighFrequencyCorrection() {
}
}
// This method CHECKs for a uniform linear array.
float Beamformer::MicSpacingFromGeometry(const std::vector<Point>& geometry) {
CHECK_GE(geometry.size(), 2u);
float mic_spacing = 0.f;
for (size_t i = 0u; i < 3u; ++i) {
float difference = geometry[1].c[i] - geometry[0].c[i];
for (size_t j = 2u; j < geometry.size(); ++j) {
CHECK_LT(geometry[j].c[i] - geometry[j - 1].c[i] - difference, 1e-6);
}
mic_spacing += difference * difference;
}
return sqrt(mic_spacing);
}
void Beamformer::EstimateTargetPresence() {
const int quantile = (1.f - kMaskQuantile) * high_average_end_bin_ +
kMaskQuantile * low_average_start_bin_;

View File

@ -13,7 +13,7 @@
#include "webrtc/common_audio/lapped_transform.h"
#include "webrtc/modules/audio_processing/beamformer/complex_matrix.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/beamformer/array_util.h"
namespace webrtc {
@ -94,7 +94,6 @@ class Beamformer : public LappedTransform::Callback {
// Applies both sets of masks to |input| and store in |output|.
void ApplyMasks(const complex_f* const* input, complex_f* const* output);
float MicSpacingFromGeometry(const std::vector<Point>& array_geometry);
void EstimateTargetPresence();
static const int kFftSize = 256;
@ -108,7 +107,8 @@ class Beamformer : public LappedTransform::Callback {
// Parameters exposed to the user.
const int num_input_channels_;
int sample_rate_hz_;
const float mic_spacing_;
const std::vector<Point> array_geometry_;
// Calculated based on user-input and constants in the .cc file.
int low_average_start_bin_;

View File

@ -28,57 +28,26 @@ float BesselJ0(float x) {
namespace webrtc {
// Calculates the boxcar-angular desired source distribution at a given
// wavenumber, and stores it in |mat|.
void CovarianceMatrixGenerator::Boxcar(float wave_number,
int num_input_channels,
float mic_spacing,
float half_width,
ComplexMatrix<float>* mat) {
CHECK_EQ(num_input_channels, mat->num_rows());
CHECK_EQ(num_input_channels, mat->num_columns());
void CovarianceMatrixGenerator::UniformCovarianceMatrix(
float wave_number,
const std::vector<Point>& geometry,
ComplexMatrix<float>* mat) {
CHECK_EQ(static_cast<int>(geometry.size()), mat->num_rows());
CHECK_EQ(static_cast<int>(geometry.size()), mat->num_columns());
complex<float>* const* boxcar_elements = mat->elements();
for (int i = 0; i < num_input_channels; ++i) {
for (int j = 0; j < num_input_channels; ++j) {
if (i == j) {
boxcar_elements[i][j] = complex<float>(2.f * half_width, 0.f);
complex<float>* const* mat_els = mat->elements();
for (size_t i = 0; i < geometry.size(); ++i) {
for (size_t j = 0; j < geometry.size(); ++j) {
if (wave_number > 0.f) {
mat_els[i][j] =
BesselJ0(wave_number * Distance(geometry[i], geometry[j]));
} else {
float factor = (j - i) * wave_number * mic_spacing;
float boxcar_real = 2.f * sin(factor * half_width) / factor;
boxcar_elements[i][j] = complex<float>(boxcar_real, 0.f);
mat_els[i][j] = i == j ? 1.f : 0.f;
}
}
}
}
void CovarianceMatrixGenerator::GappedUniformCovarianceMatrix(
float wave_number,
float num_input_channels,
float mic_spacing,
float gap_half_width,
ComplexMatrix<float>* mat) {
CHECK_EQ(num_input_channels, mat->num_rows());
CHECK_EQ(num_input_channels, mat->num_columns());
complex<float>* const* mat_els = mat->elements();
for (int i = 0; i < num_input_channels; ++i) {
for (int j = 0; j < num_input_channels; ++j) {
float x = (j - i) * wave_number * mic_spacing;
mat_els[i][j] = BesselJ0(x);
}
}
ComplexMatrix<float> boxcar_mat(num_input_channels, num_input_channels);
CovarianceMatrixGenerator::Boxcar(wave_number,
num_input_channels,
mic_spacing,
gap_half_width,
&boxcar_mat);
mat->Subtract(boxcar_mat);
}
void CovarianceMatrixGenerator::AngledCovarianceMatrix(
float sound_speed,
float angle,
@ -86,66 +55,44 @@ void CovarianceMatrixGenerator::AngledCovarianceMatrix(
int fft_size,
int num_freq_bins,
int sample_rate,
int num_input_channels,
float mic_spacing,
const std::vector<Point>& geometry,
ComplexMatrix<float>* mat) {
CHECK_EQ(num_input_channels, mat->num_rows());
CHECK_EQ(num_input_channels, mat->num_columns());
CHECK_EQ(static_cast<int>(geometry.size()), mat->num_rows());
CHECK_EQ(static_cast<int>(geometry.size()), mat->num_columns());
ComplexMatrix<float> interf_cov_vector(1, num_input_channels);
ComplexMatrix<float> interf_cov_vector_transposed(num_input_channels, 1);
ComplexMatrix<float> interf_cov_vector(1, geometry.size());
ComplexMatrix<float> interf_cov_vector_transposed(geometry.size(), 1);
PhaseAlignmentMasks(frequency_bin,
fft_size,
sample_rate,
sound_speed,
mic_spacing,
num_input_channels,
sin(angle),
geometry,
angle,
&interf_cov_vector);
interf_cov_vector_transposed.Transpose(interf_cov_vector);
interf_cov_vector.PointwiseConjugate();
mat->Multiply(interf_cov_vector_transposed, interf_cov_vector);
}
void CovarianceMatrixGenerator::DCCovarianceMatrix(int num_input_channels,
float half_width,
ComplexMatrix<float>* mat) {
CHECK_EQ(num_input_channels, mat->num_rows());
CHECK_EQ(num_input_channels, mat->num_columns());
complex<float>* const* elements = mat->elements();
float diagonal_value = 1.f - 2.f * half_width;
for (int i = 0; i < num_input_channels; ++i) {
for (int j = 0; j < num_input_channels; ++j) {
if (i == j) {
elements[i][j] = complex<float>(diagonal_value, 0.f);
} else {
elements[i][j] = complex<float>(0.f, 0.f);
}
}
}
}
void CovarianceMatrixGenerator::PhaseAlignmentMasks(int frequency_bin,
int fft_size,
int sample_rate,
float sound_speed,
float mic_spacing,
int num_input_channels,
float sin_angle,
ComplexMatrix<float>* mat) {
void CovarianceMatrixGenerator::PhaseAlignmentMasks(
int frequency_bin,
int fft_size,
int sample_rate,
float sound_speed,
const std::vector<Point>& geometry,
float angle,
ComplexMatrix<float>* mat) {
CHECK_EQ(1, mat->num_rows());
CHECK_EQ(num_input_channels, mat->num_columns());
CHECK_EQ(static_cast<int>(geometry.size()), mat->num_columns());
float freq_in_hertz =
(static_cast<float>(frequency_bin) / fft_size) * sample_rate;
complex<float>* const* mat_els = mat->elements();
for (int c_ix = 0; c_ix < num_input_channels; ++c_ix) {
// TODO(aluebs): Generalize for non-uniform-linear microphone arrays.
float distance = mic_spacing * c_ix * sin_angle * -1.f;
float phase_shift = 2 * M_PI * distance * freq_in_hertz / sound_speed;
for (size_t c_ix = 0; c_ix < geometry.size(); ++c_ix) {
float distance = std::cos(angle) * geometry[c_ix].x() +
std::sin(angle) * geometry[c_ix].y();
float phase_shift = -2.f * M_PI * distance * freq_in_hertz / sound_speed;
// Euler's formula for mat[0][c_ix] = e^(j * phase_shift).
mat_els[0][c_ix] = complex<float>(cos(phase_shift), sin(phase_shift));

View File

@ -12,6 +12,7 @@
#define WEBRTC_MODULES_AUDIO_PROCESSING_BEAMFORMER_COVARIANCE_MATRIX_GENERATOR_H_
#include "webrtc/modules/audio_processing/beamformer/complex_matrix.h"
#include "webrtc/modules/audio_processing/beamformer/array_util.h"
namespace webrtc {
@ -20,23 +21,11 @@ namespace webrtc {
// |num_input_channels| x |num_input_channels|.
class CovarianceMatrixGenerator {
public:
// Generates the covariance matrix of the target. The boxcar implementation
// suppresses some high-frequency distortion caused by narrow high-frequency
// suppression bands turning on/off too quickly. WARNING: The target angle is
// assumed to be 0.
static void Boxcar(float wave_number,
int num_input_channels,
float mic_spacing,
float half_width,
ComplexMatrix<float>* mat);
// A uniform covariance matrix with a gap at the target location. WARNING:
// The target angle is assumed to be 0.
static void GappedUniformCovarianceMatrix(float wave_number,
float num_input_channels,
float mic_spacing,
float gap_half_width,
ComplexMatrix<float>* mat);
static void UniformCovarianceMatrix(float wave_number,
const std::vector<Point>& geometry,
ComplexMatrix<float>* mat);
// The covariance matrix of a source at the given angle.
static void AngledCovarianceMatrix(float sound_speed,
@ -45,15 +34,9 @@ class CovarianceMatrixGenerator {
int fft_size,
int num_freq_bins,
int sample_rate,
int num_input_channels,
float mic_spacing,
const std::vector<Point>& geometry,
ComplexMatrix<float>* mat);
// A base-case covariance matrix for when the frequency is 0 Hertz.
static void DCCovarianceMatrix(int num_input_channels,
float half_width,
ComplexMatrix<float>* mat);
// Calculates phase shifts that, when applied to a multichannel signal and
// added together, cause constructive interferernce for sources located at
// the given angle.
@ -61,9 +44,8 @@ class CovarianceMatrixGenerator {
int fft_size,
int sample_rate,
float sound_speed,
float mic_spacing,
int num_input_channels,
float sin_angle,
const std::vector<Point>& geometry,
float angle,
ComplexMatrix<float>* mat);
};

View File

@ -21,97 +21,27 @@ namespace webrtc {
using std::complex;
TEST(CovarianceMatrixGeneratorTest, TestBoxcar) {
const float kWaveNumber = 10.3861f;
const int kNumberMics = 3;
const float kMicSpacing = 0.23f;
const float kHalfWidth = 0.001f;
const float kTolerance = 0.0001f;
ComplexMatrix<float> actual_boxcar(kNumberMics, kNumberMics);
CovarianceMatrixGenerator::Boxcar(
kWaveNumber, kNumberMics, kMicSpacing, kHalfWidth, &actual_boxcar);
complex<float>* const* actual_els = actual_boxcar.elements();
EXPECT_NEAR(actual_els[0][0].real(), 0.002f, kTolerance);
EXPECT_NEAR(actual_els[0][1].real(), 0.002f, kTolerance);
EXPECT_NEAR(actual_els[0][2].real(), 0.002f, kTolerance);
EXPECT_NEAR(actual_els[1][0].real(), 0.002f, kTolerance);
EXPECT_NEAR(actual_els[1][1].real(), 0.002f, kTolerance);
EXPECT_NEAR(actual_els[1][2].real(), 0.002f, kTolerance);
EXPECT_NEAR(actual_els[2][0].real(), 0.002f, kTolerance);
EXPECT_NEAR(actual_els[2][1].real(), 0.002f, kTolerance);
EXPECT_NEAR(actual_els[2][2].real(), 0.002f, kTolerance);
EXPECT_NEAR(actual_els[0][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][2].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][2].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][2].imag(), 0.f, kTolerance);
}
// Test Boxcar with large numbers to generate matrix with varying
// numbers.
TEST(CovarianceMatrixGeneratorTest, TestBoxcarLargeNumbers) {
const float kWaveNumber = 10.3861f;
const int kNumberMics = 3;
const float kMicSpacing = 3.f;
const float kHalfWidth = 0.1f;
const float kTolerance = 0.0001f;
ComplexMatrix<float> actual_boxcar(kNumberMics, kNumberMics);
CovarianceMatrixGenerator::Boxcar(
kWaveNumber, kNumberMics, kMicSpacing, kHalfWidth, &actual_boxcar);
complex<float>* const* actual_els = actual_boxcar.elements();
EXPECT_NEAR(actual_els[0][0].real(), 0.2f, kTolerance);
EXPECT_NEAR(actual_els[0][1].real(), 0.0017f, kTolerance);
EXPECT_NEAR(actual_els[0][2].real(), -0.0017f, kTolerance);
EXPECT_NEAR(actual_els[1][0].real(), 0.0017f, kTolerance);
EXPECT_NEAR(actual_els[1][1].real(), 0.2f, kTolerance);
EXPECT_NEAR(actual_els[1][2].real(), 0.0017f, kTolerance);
EXPECT_NEAR(actual_els[2][0].real(), -0.0017f, kTolerance);
EXPECT_NEAR(actual_els[2][1].real(), 0.0017f, kTolerance);
EXPECT_NEAR(actual_els[2][2].real(), 0.2f, kTolerance);
EXPECT_NEAR(actual_els[0][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][2].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][2].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][2].imag(), 0.f, kTolerance);
}
TEST(CovarianceMatrixGeneratorTest, TestGappedUniformCovarianceMatrix2Mics) {
TEST(CovarianceMatrixGeneratorTest, TestUniformCovarianceMatrix2Mics) {
const float kWaveNumber = 0.5775f;
const int kNumberMics = 2;
const float kMicSpacing = 0.05f;
const float kHalfWidth = 0.001f;
const float kTolerance = 0.0001f;
std::vector<Point> geometry;
float first_mic = (kNumberMics - 1) * kMicSpacing / 2.f;
for (int i = 0; i < kNumberMics; ++i) {
geometry.push_back(Point(i * kMicSpacing - first_mic, 0.f, 0.f));
}
ComplexMatrix<float> actual_covariance_matrix(kNumberMics, kNumberMics);
CovarianceMatrixGenerator::GappedUniformCovarianceMatrix(
kWaveNumber,
kNumberMics,
kMicSpacing,
kHalfWidth,
&actual_covariance_matrix);
CovarianceMatrixGenerator::UniformCovarianceMatrix(kWaveNumber,
geometry,
&actual_covariance_matrix);
complex<float>* const* actual_els = actual_covariance_matrix.elements();
EXPECT_NEAR(actual_els[0][0].real(), 0.998f, kTolerance);
EXPECT_NEAR(actual_els[0][1].real(), 0.9978f, kTolerance);
EXPECT_NEAR(actual_els[1][0].real(), 0.9978f, kTolerance);
EXPECT_NEAR(actual_els[1][1].real(), 0.998f, kTolerance);
EXPECT_NEAR(actual_els[0][0].real(), 1.f, kTolerance);
EXPECT_NEAR(actual_els[0][1].real(), 0.9998f, kTolerance);
EXPECT_NEAR(actual_els[1][0].real(), 0.9998f, kTolerance);
EXPECT_NEAR(actual_els[1][1].real(), 1.f, kTolerance);
EXPECT_NEAR(actual_els[0][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][1].imag(), 0.f, kTolerance);
@ -119,32 +49,32 @@ TEST(CovarianceMatrixGeneratorTest, TestGappedUniformCovarianceMatrix2Mics) {
EXPECT_NEAR(actual_els[1][1].imag(), 0.f, kTolerance);
}
TEST(CovarianceMatrixGeneratorTest, TestGappedUniformCovarianceMatrix3Mics) {
TEST(CovarianceMatrixGeneratorTest, TestUniformCovarianceMatrix3Mics) {
const float kWaveNumber = 10.3861f;
const int kNumberMics = 3;
const float kMicSpacing = 0.04f;
const float kHalfWidth = 0.001f;
const float kTolerance = 0.0001f;
std::vector<Point> geometry;
float first_mic = (kNumberMics - 1) * kMicSpacing / 2.f;
for (int i = 0; i < kNumberMics; ++i) {
geometry.push_back(Point(i * kMicSpacing - first_mic, 0.f, 0.f));
}
ComplexMatrix<float> actual_covariance_matrix(kNumberMics, kNumberMics);
CovarianceMatrixGenerator::GappedUniformCovarianceMatrix(
kWaveNumber,
kNumberMics,
kMicSpacing,
kHalfWidth,
&actual_covariance_matrix);
CovarianceMatrixGenerator::UniformCovarianceMatrix(kWaveNumber,
geometry,
&actual_covariance_matrix);
complex<float>* const* actual_els = actual_covariance_matrix.elements();
EXPECT_NEAR(actual_els[0][0].real(), 0.998f, kTolerance);
EXPECT_NEAR(actual_els[0][1].real(), 0.9553f, kTolerance);
EXPECT_NEAR(actual_els[0][2].real(), 0.8327f, kTolerance);
EXPECT_NEAR(actual_els[1][0].real(), 0.9553f, kTolerance);
EXPECT_NEAR(actual_els[1][1].real(), 0.998f, kTolerance);
EXPECT_NEAR(actual_els[1][2].real(), 0.9553f, kTolerance);
EXPECT_NEAR(actual_els[2][0].real(), 0.8327f, kTolerance);
EXPECT_NEAR(actual_els[2][1].real(), 0.9553f, kTolerance);
EXPECT_NEAR(actual_els[2][2].real(), 0.998f, kTolerance);
EXPECT_NEAR(actual_els[0][0].real(), 1.f, kTolerance);
EXPECT_NEAR(actual_els[0][1].real(), 0.9573f, kTolerance);
EXPECT_NEAR(actual_els[0][2].real(), 0.8347f, kTolerance);
EXPECT_NEAR(actual_els[1][0].real(), 0.9573f, kTolerance);
EXPECT_NEAR(actual_els[1][1].real(), 1.f, kTolerance);
EXPECT_NEAR(actual_els[1][2].real(), 0.9573f, kTolerance);
EXPECT_NEAR(actual_els[2][0].real(), 0.8347f, kTolerance);
EXPECT_NEAR(actual_els[2][1].real(), 0.9573f, kTolerance);
EXPECT_NEAR(actual_els[2][2].real(), 1.f, kTolerance);
EXPECT_NEAR(actual_els[0][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][1].imag(), 0.f, kTolerance);
@ -157,6 +87,57 @@ TEST(CovarianceMatrixGeneratorTest, TestGappedUniformCovarianceMatrix3Mics) {
EXPECT_NEAR(actual_els[2][2].imag(), 0.f, kTolerance);
}
TEST(CovarianceMatrixGeneratorTest, TestUniformCovarianceMatrix3DArray) {
const float kWaveNumber = 1.2345f;
const int kNumberMics = 4;
const float kTolerance = 0.0001f;
std::vector<Point> geometry;
geometry.push_back(Point(-0.025f, -0.05f, -0.075f));
geometry.push_back(Point(0.075f, -0.05f, -0.075f));
geometry.push_back(Point(-0.025f, 0.15f, -0.075f));
geometry.push_back(Point(-0.025f, -0.05f, 0.225f));
ComplexMatrix<float> actual_covariance_matrix(kNumberMics, kNumberMics);
CovarianceMatrixGenerator::UniformCovarianceMatrix(kWaveNumber,
geometry,
&actual_covariance_matrix);
complex<float>* const* actual_els = actual_covariance_matrix.elements();
EXPECT_NEAR(actual_els[0][0].real(), 1.f, kTolerance);
EXPECT_NEAR(actual_els[0][1].real(), 0.9962f, kTolerance);
EXPECT_NEAR(actual_els[0][2].real(), 0.9848f, kTolerance);
EXPECT_NEAR(actual_els[0][3].real(), 0.9660f, kTolerance);
EXPECT_NEAR(actual_els[1][0].real(), 0.9962f, kTolerance);
EXPECT_NEAR(actual_els[1][1].real(), 1.f, kTolerance);
EXPECT_NEAR(actual_els[1][2].real(), 0.9810f, kTolerance);
EXPECT_NEAR(actual_els[1][3].real(), 0.9623f, kTolerance);
EXPECT_NEAR(actual_els[2][0].real(), 0.9848f, kTolerance);
EXPECT_NEAR(actual_els[2][1].real(), 0.9810f, kTolerance);
EXPECT_NEAR(actual_els[2][2].real(), 1.f, kTolerance);
EXPECT_NEAR(actual_els[2][3].real(), 0.9511f, kTolerance);
EXPECT_NEAR(actual_els[3][0].real(), 0.9660f, kTolerance);
EXPECT_NEAR(actual_els[3][1].real(), 0.9623f, kTolerance);
EXPECT_NEAR(actual_els[3][2].real(), 0.9511f, kTolerance);
EXPECT_NEAR(actual_els[3][3].real(), 1.f, kTolerance);
EXPECT_NEAR(actual_els[0][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][2].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][3].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][2].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][3].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][2].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[2][3].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[3][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[3][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[3][2].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[3][3].imag(), 0.f, kTolerance);
}
TEST(CovarianceMatrixGeneratorTest, TestAngledCovarianceMatrix2Mics) {
const float kSpeedOfSound = 340;
const float kAngle = static_cast<float>(M_PI) / 4.f;
@ -167,6 +148,11 @@ TEST(CovarianceMatrixGeneratorTest, TestAngledCovarianceMatrix2Mics) {
const int kNumberMics = 2;
const float kMicSpacing = 0.04f;
const float kTolerance = 0.0001f;
std::vector<Point> geometry;
float first_mic = (kNumberMics - 1) * kMicSpacing / 2.f;
for (int i = 0; i < kNumberMics; ++i) {
geometry.push_back(Point(i * kMicSpacing - first_mic, 0.f, 0.f));
}
ComplexMatrix<float> actual_covariance_matrix(kNumberMics, kNumberMics);
CovarianceMatrixGenerator::AngledCovarianceMatrix(kSpeedOfSound,
kAngle,
@ -174,8 +160,7 @@ TEST(CovarianceMatrixGeneratorTest, TestAngledCovarianceMatrix2Mics) {
kFftSize,
kNumberFrequencyBins,
kSampleRate,
kNumberMics,
kMicSpacing,
geometry,
&actual_covariance_matrix);
complex<float>* const* actual_els = actual_covariance_matrix.elements();
@ -201,7 +186,11 @@ TEST(CovarianceMatrixGeneratorTest, TestAngledCovarianceMatrix3Mics) {
const int kNumberMics = 3;
const float kMicSpacing = 0.05f;
const float kTolerance = 0.0001f;
std::vector<Point> geometry;
float first_mic = (kNumberMics - 1) * kMicSpacing / 2.f;
for (int i = 0; i < kNumberMics; ++i) {
geometry.push_back(Point(i * kMicSpacing - first_mic, 0.f, 0.f));
}
ComplexMatrix<float> actual_covariance_matrix(kNumberMics, kNumberMics);
CovarianceMatrixGenerator::AngledCovarianceMatrix(kSpeedOfSound,
kAngle,
@ -209,8 +198,7 @@ TEST(CovarianceMatrixGeneratorTest, TestAngledCovarianceMatrix3Mics) {
kFftSize,
kNumberFrequencyBins,
kSampleRate,
kNumberMics,
kMicSpacing,
geometry,
&actual_covariance_matrix);
complex<float>* const* actual_els = actual_covariance_matrix.elements();
@ -236,27 +224,6 @@ TEST(CovarianceMatrixGeneratorTest, TestAngledCovarianceMatrix3Mics) {
EXPECT_NEAR(actual_els[2][2].imag(), 0.f, kTolerance);
}
TEST(CovarianceMatrixGeneratorTest, TestDCCovarianceMatrix) {
const int kNumberMics = 2;
const float kHalfWidth = 0.01f;
ComplexMatrix<float> actual_covariance_matrix(kNumberMics, kNumberMics);
CovarianceMatrixGenerator::DCCovarianceMatrix(
kNumberMics, kHalfWidth, &actual_covariance_matrix);
complex<float>* const* actual_els = actual_covariance_matrix.elements();
EXPECT_NEAR(actual_els[0][0].real(), 0.98f, kTolerance);
EXPECT_NEAR(actual_els[0][1].real(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][0].real(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][1].real(), 0.98f, kTolerance);
EXPECT_NEAR(actual_els[0][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[0][1].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][0].imag(), 0.f, kTolerance);
EXPECT_NEAR(actual_els[1][1].imag(), 0.f, kTolerance);
}
// PhaseAlignmentMasks is tested by AngledCovarianceMatrix and by
// InitBeamformerWeights in BeamformerUnittest.

View File

@ -17,6 +17,7 @@
#include "webrtc/base/platform_file.h"
#include "webrtc/common.h"
#include "webrtc/modules/audio_processing/beamformer/array_util.h"
#include "webrtc/typedefs.h"
struct AecCore;
@ -84,16 +85,6 @@ struct ExperimentalNs {
bool enabled;
};
// Coordinates in meters.
struct Point {
Point(float x, float y, float z) {
c[0] = x;
c[1] = y;
c[2] = z;
}
float c[3];
};
// Use to enable beamforming. Must be provided through the constructor. It will
// have no impact if used with AudioProcessing::SetExtraOptions().
struct Beamforming {

View File

@ -101,7 +101,7 @@ std::vector<Point> get_array_geometry(size_t num_mics) {
"mic_spacing must a positive value when beamforming is enabled.\n");
} else {
for (size_t i = 0; i < num_mics; ++i) {
result.push_back(Point(0.0, i * FLAGS_mic_spacing, 0.0));
result.push_back(Point(i * FLAGS_mic_spacing, 0.f, 0.f));
}
}
}