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 000000000..8d1cda783 --- /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 + +namespace webrtc { + +// Coordinates in meters. +template +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 Point; + +template +float Distance(CartesianPoint a, CartesianPoint 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 6cbe6127d..6d78de9b8 100644 --- a/webrtc/modules/audio_processing/beamformer/beamformer.cc +++ b/webrtc/modules/audio_processing/beamformer/beamformer.cc @@ -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(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(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& mat) { return sum_squares; } +// Does |out| = |in|.' * conj(|in|) for row vector |in|. +void TransposedConjugatedProduct(const ComplexMatrix& in, + ComplexMatrix* 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* in_elements = in.elements()[0]; + complex* 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 GetCenteredArray(std::vector 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& 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& 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 c3b32ffcd..3407bd346 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 @@ 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& 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 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 8d592a5c2..c70bf5e7a 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 @@ 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* 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& geometry, + ComplexMatrix* mat) { + CHECK_EQ(static_cast(geometry.size()), mat->num_rows()); + CHECK_EQ(static_cast(geometry.size()), mat->num_columns()); - complex* 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(2.f * half_width, 0.f); + complex* 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(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* mat) { - CHECK_EQ(num_input_channels, mat->num_rows()); - CHECK_EQ(num_input_channels, mat->num_columns()); - - complex* 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 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& geometry, ComplexMatrix* mat) { - CHECK_EQ(num_input_channels, mat->num_rows()); - CHECK_EQ(num_input_channels, mat->num_columns()); + CHECK_EQ(static_cast(geometry.size()), mat->num_rows()); + CHECK_EQ(static_cast(geometry.size()), mat->num_columns()); - ComplexMatrix interf_cov_vector(1, num_input_channels); - ComplexMatrix interf_cov_vector_transposed(num_input_channels, 1); + ComplexMatrix interf_cov_vector(1, geometry.size()); + ComplexMatrix 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* mat) { - CHECK_EQ(num_input_channels, mat->num_rows()); - CHECK_EQ(num_input_channels, mat->num_columns()); - - complex* 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(diagonal_value, 0.f); - } else { - elements[i][j] = complex(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* mat) { +void CovarianceMatrixGenerator::PhaseAlignmentMasks( + int frequency_bin, + int fft_size, + int sample_rate, + float sound_speed, + const std::vector& geometry, + float angle, + ComplexMatrix* mat) { CHECK_EQ(1, mat->num_rows()); - CHECK_EQ(num_input_channels, mat->num_columns()); + CHECK_EQ(static_cast(geometry.size()), mat->num_columns()); float freq_in_hertz = (static_cast(frequency_bin) / fft_size) * sample_rate; complex* 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(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 a3bceef67..597946275 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 @@ 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* 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* mat); + static void UniformCovarianceMatrix(float wave_number, + const std::vector& geometry, + ComplexMatrix* 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& geometry, ComplexMatrix* mat); - // A base-case covariance matrix for when the frequency is 0 Hertz. - static void DCCovarianceMatrix(int num_input_channels, - float half_width, - ComplexMatrix* 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& geometry, + float angle, ComplexMatrix* 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 0c3a1d56b..4ea341d84 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 @@ 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 actual_boxcar(kNumberMics, kNumberMics); - CovarianceMatrixGenerator::Boxcar( - kWaveNumber, kNumberMics, kMicSpacing, kHalfWidth, &actual_boxcar); - - complex* 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 actual_boxcar(kNumberMics, kNumberMics); - CovarianceMatrixGenerator::Boxcar( - kWaveNumber, kNumberMics, kMicSpacing, kHalfWidth, &actual_boxcar); - - complex* 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 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 actual_covariance_matrix(kNumberMics, kNumberMics); - CovarianceMatrixGenerator::GappedUniformCovarianceMatrix( - kWaveNumber, - kNumberMics, - kMicSpacing, - kHalfWidth, - &actual_covariance_matrix); + CovarianceMatrixGenerator::UniformCovarianceMatrix(kWaveNumber, + geometry, + &actual_covariance_matrix); complex* 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 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 actual_covariance_matrix(kNumberMics, kNumberMics); - CovarianceMatrixGenerator::GappedUniformCovarianceMatrix( - kWaveNumber, - kNumberMics, - kMicSpacing, - kHalfWidth, - &actual_covariance_matrix); + CovarianceMatrixGenerator::UniformCovarianceMatrix(kWaveNumber, + geometry, + &actual_covariance_matrix); complex* 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 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 actual_covariance_matrix(kNumberMics, kNumberMics); + CovarianceMatrixGenerator::UniformCovarianceMatrix(kWaveNumber, + geometry, + &actual_covariance_matrix); + + complex* 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(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 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 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* 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 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 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* 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 actual_covariance_matrix(kNumberMics, kNumberMics); - CovarianceMatrixGenerator::DCCovarianceMatrix( - kNumberMics, kHalfWidth, &actual_covariance_matrix); - - complex* 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 4004a624f..049cfbbbf 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 @@ 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 { diff --git a/webrtc/modules/audio_processing/test/audioproc_float.cc b/webrtc/modules/audio_processing/test/audioproc_float.cc index 805d884ef..a451d0a4c 100644 --- a/webrtc/modules/audio_processing/test/audioproc_float.cc +++ b/webrtc/modules/audio_processing/test/audioproc_float.cc @@ -101,7 +101,7 @@ std::vector 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)); } } }