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
diff --git a/webrtc/modules/audio_processing/beamformer/array_util.h b/webrtc/modules/audio_processing/beamformer/array_util.h
new file mode 100644
index 0000000..8d1cda7
--- /dev/null
+++ b/webrtc/modules/audio_processing/beamformer/array_util.h
@@ -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_
diff --git a/webrtc/modules/audio_processing/beamformer/beamformer.cc b/webrtc/modules/audio_processing/beamformer/beamformer.cc
index 6cbe612..6d78de9 100644
--- a/webrtc/modules/audio_processing/beamformer/beamformer.cc
+++ b/webrtc/modules/audio_processing/beamformer/beamformer.cc
@@ -30,15 +30,16 @@
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 @@
// 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 @@
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::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::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 @@
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 @@
}
}
-// 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_;
diff --git a/webrtc/modules/audio_processing/beamformer/beamformer.h b/webrtc/modules/audio_processing/beamformer/beamformer.h
index c3b32ff..3407bd3 100644
--- a/webrtc/modules/audio_processing/beamformer/beamformer.h
+++ b/webrtc/modules/audio_processing/beamformer/beamformer.h
@@ -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 @@
// 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 @@
// 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_;
diff --git a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.cc b/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.cc
index 8d592a5..c70bf5e 100644
--- a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.cc
+++ b/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.cc
@@ -28,57 +28,26 @@
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 @@
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));
diff --git a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h b/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h
index a3bceef..5979462 100644
--- a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h
+++ b/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator.h
@@ -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 @@
// |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 @@
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 @@
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);
};
diff --git a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator_unittest.cc b/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator_unittest.cc
index 0c3a1d5..4ea341d 100644
--- a/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator_unittest.cc
+++ b/webrtc/modules/audio_processing/beamformer/covariance_matrix_generator_unittest.cc
@@ -21,97 +21,27 @@
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 @@
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 @@
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 @@
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 @@
kFftSize,
kNumberFrequencyBins,
kSampleRate,
- kNumberMics,
- kMicSpacing,
+ geometry,
&actual_covariance_matrix);
complex<float>* const* actual_els = actual_covariance_matrix.elements();
@@ -201,7 +186,11 @@
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 @@
kFftSize,
kNumberFrequencyBins,
kSampleRate,
- kNumberMics,
- kMicSpacing,
+ geometry,
&actual_covariance_matrix);
complex<float>* const* actual_els = actual_covariance_matrix.elements();
@@ -236,27 +224,6 @@
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.
diff --git a/webrtc/modules/audio_processing/include/audio_processing.h b/webrtc/modules/audio_processing/include/audio_processing.h
index 4004a62..049cfbb 100644
--- a/webrtc/modules/audio_processing/include/audio_processing.h
+++ b/webrtc/modules/audio_processing/include/audio_processing.h
@@ -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 @@
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 {
diff --git a/webrtc/modules/audio_processing/test/audioproc_float.cc b/webrtc/modules/audio_processing/test/audioproc_float.cc
index 805d884..a451d0a 100644
--- a/webrtc/modules/audio_processing/test/audioproc_float.cc
+++ b/webrtc/modules/audio_processing/test/audioproc_float.cc
@@ -101,7 +101,7 @@
"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));
}
}
}