| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
| 5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
| 6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
| 7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
| 8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 // MSVC++ requires this to be set before any other includes to get M_PI. | 11 // MSVC++ requires this to be set before any other includes to get M_PI. |
| 12 #define _USE_MATH_DEFINES | 12 #define _USE_MATH_DEFINES |
| 13 | 13 |
| 14 #include "webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h" | 14 #include "webrtc/modules/audio_processing/beamformer/nonlinear_beamformer.h" |
| 15 | 15 |
| 16 #include <math.h> | 16 #include <math.h> |
| 17 | 17 |
| 18 #include "testing/gtest/include/gtest/gtest.h" | 18 #include "testing/gtest/include/gtest/gtest.h" |
| 19 #include "webrtc/base/array_view.h" |
| 20 #include "webrtc/modules/audio_processing/audio_buffer.h" |
| 21 #include "webrtc/modules/audio_processing/test/audio_buffer_tools.h" |
| 22 #include "webrtc/modules/audio_processing/test/bitexactness_tools.h" |
| 19 | 23 |
| 20 namespace webrtc { | 24 namespace webrtc { |
| 21 namespace { | 25 namespace { |
| 22 | 26 |
| 23 const int kChunkSizeMs = 10; | 27 const int kChunkSizeMs = 10; |
| 24 const int kSampleRateHz = 16000; | 28 const int kSampleRateHz = 16000; |
| 25 | 29 |
| 26 SphericalPointf AzimuthToSphericalPoint(float azimuth_radians) { | 30 SphericalPointf AzimuthToSphericalPoint(float azimuth_radians) { |
| 27 return SphericalPointf(azimuth_radians, 0.f, 1.f); | 31 return SphericalPointf(azimuth_radians, 0.f, 1.f); |
| 28 } | 32 } |
| (...skipping 12 matching lines...) Expand all Loading... |
| 41 EXPECT_FALSE(bf->IsInBeam(AzimuthToSphericalPoint( | 45 EXPECT_FALSE(bf->IsInBeam(AzimuthToSphericalPoint( |
| 42 target_azimuth_radians + NonlinearBeamformer::kHalfBeamWidthRadians + | 46 target_azimuth_radians + NonlinearBeamformer::kHalfBeamWidthRadians + |
| 43 0.001f))); | 47 0.001f))); |
| 44 } | 48 } |
| 45 | 49 |
| 46 void AimAndVerify(NonlinearBeamformer* bf, float target_azimuth_radians) { | 50 void AimAndVerify(NonlinearBeamformer* bf, float target_azimuth_radians) { |
| 47 bf->AimAt(AzimuthToSphericalPoint(target_azimuth_radians)); | 51 bf->AimAt(AzimuthToSphericalPoint(target_azimuth_radians)); |
| 48 Verify(bf, target_azimuth_radians); | 52 Verify(bf, target_azimuth_radians); |
| 49 } | 53 } |
| 50 | 54 |
| 55 // Bitexactness test code. |
| 56 const size_t kNumFramesToProcess = 1000; |
| 57 |
| 58 void ProcessOneFrame(int sample_rate_hz, |
| 59 AudioBuffer* capture_audio_buffer, |
| 60 Beamformer<float>* beamformer) { |
| 61 if (sample_rate_hz > AudioProcessing::kSampleRate16kHz) { |
| 62 capture_audio_buffer->SplitIntoFrequencyBands(); |
| 63 } |
| 64 |
| 65 beamformer->ProcessChunk(*capture_audio_buffer->split_data_f(), |
| 66 capture_audio_buffer->split_data_f()); |
| 67 capture_audio_buffer->set_num_channels(1); |
| 68 |
| 69 if (sample_rate_hz > AudioProcessing::kSampleRate16kHz) { |
| 70 capture_audio_buffer->MergeFrequencyBands(); |
| 71 } |
| 72 } |
| 73 |
| 74 int BeamformerSampleRate(int sample_rate_hz) { |
| 75 return (sample_rate_hz > AudioProcessing::kSampleRate16kHz |
| 76 ? AudioProcessing::kSampleRate16kHz |
| 77 : sample_rate_hz); |
| 78 } |
| 79 |
| 80 void RunBitExactnessTest(int sample_rate_hz, |
| 81 const std::vector<Point>& array_geometry, |
| 82 const SphericalPointf& target_direction, |
| 83 rtc::ArrayView<const float> output_reference) { |
| 84 NonlinearBeamformer beamformer(array_geometry, target_direction); |
| 85 beamformer.Initialize(AudioProcessing::kChunkSizeMs, |
| 86 BeamformerSampleRate(sample_rate_hz)); |
| 87 |
| 88 const StreamConfig capture_config(sample_rate_hz, array_geometry.size(), |
| 89 false); |
| 90 AudioBuffer capture_buffer( |
| 91 capture_config.num_frames(), capture_config.num_channels(), |
| 92 capture_config.num_frames(), capture_config.num_channels(), |
| 93 capture_config.num_frames()); |
| 94 test::InputAudioFile capture_file( |
| 95 test::GetApmCaptureTestVectorFileName(sample_rate_hz)); |
| 96 std::vector<float> capture_input(capture_config.num_frames() * |
| 97 capture_config.num_channels()); |
| 98 for (size_t frame_no = 0u; frame_no < kNumFramesToProcess; ++frame_no) { |
| 99 ReadFloatSamplesFromStereoFile(capture_config.num_frames(), |
| 100 capture_config.num_channels(), &capture_file, |
| 101 capture_input); |
| 102 |
| 103 test::CopyVectorToAudioBuffer(capture_config, capture_input, |
| 104 &capture_buffer); |
| 105 |
| 106 ProcessOneFrame(sample_rate_hz, &capture_buffer, &beamformer); |
| 107 } |
| 108 |
| 109 // Extract and verify the test results. |
| 110 std::vector<float> capture_output; |
| 111 test::ExtractVectorFromAudioBuffer(capture_config, &capture_buffer, |
| 112 &capture_output); |
| 113 |
| 114 const float kTolerance = 1.f / static_cast<float>(1 << 15); |
| 115 |
| 116 // Compare the output with the reference. Only the first values of the output |
| 117 // from last frame processed are compared in order not having to specify all |
| 118 // preceeding frames as testvectors. As the algorithm being tested has a |
| 119 // memory, testing only the last frame implicitly also tests the preceeding |
| 120 // frames. |
| 121 EXPECT_TRUE(test::BitExactFrame( |
| 122 capture_config.num_frames(), capture_config.num_channels(), |
| 123 output_reference, capture_output, kTolerance)); |
| 124 } |
| 125 |
| 126 std::vector<Point> CreateArrayGeometry(int variant) { |
| 127 std::vector<Point> array_geometry; |
| 128 switch (variant) { |
| 129 case 1: |
| 130 array_geometry.push_back(Point(-0.025f, 0.f, 0.f)); |
| 131 array_geometry.push_back(Point(0.025f, 0.f, 0.f)); |
| 132 break; |
| 133 case 2: |
| 134 array_geometry.push_back(Point(-0.035f, 0.f, 0.f)); |
| 135 array_geometry.push_back(Point(0.035f, 0.f, 0.f)); |
| 136 break; |
| 137 case 3: |
| 138 array_geometry.push_back(Point(-0.5f, 0.f, 0.f)); |
| 139 array_geometry.push_back(Point(0.5f, 0.f, 0.f)); |
| 140 break; |
| 141 default: |
| 142 RTC_CHECK(false); |
| 143 } |
| 144 return array_geometry; |
| 145 } |
| 146 |
| 147 const SphericalPointf TargetDirection1(0.4f * static_cast<float>(M_PI) / 2.f, |
| 148 0.f, |
| 149 1.f); |
| 150 const SphericalPointf TargetDirection2(static_cast<float>(M_PI) / 2.f, |
| 151 1.f, |
| 152 2.f); |
| 153 |
| 51 } // namespace | 154 } // namespace |
| 52 | 155 |
| 53 TEST(NonlinearBeamformerTest, AimingModifiesBeam) { | 156 TEST(NonlinearBeamformerTest, AimingModifiesBeam) { |
| 54 std::vector<Point> array_geometry; | 157 std::vector<Point> array_geometry; |
| 55 array_geometry.push_back(Point(-0.025f, 0.f, 0.f)); | 158 array_geometry.push_back(Point(-0.025f, 0.f, 0.f)); |
| 56 array_geometry.push_back(Point(0.025f, 0.f, 0.f)); | 159 array_geometry.push_back(Point(0.025f, 0.f, 0.f)); |
| 57 NonlinearBeamformer bf(array_geometry); | 160 NonlinearBeamformer bf(array_geometry); |
| 58 bf.Initialize(kChunkSizeMs, kSampleRateHz); | 161 bf.Initialize(kChunkSizeMs, kSampleRateHz); |
| 59 // The default constructor parameter sets the target angle to PI / 2. | 162 // The default constructor parameter sets the target angle to PI / 2. |
| 60 Verify(&bf, static_cast<float>(M_PI) / 2.f); | 163 Verify(&bf, static_cast<float>(M_PI) / 2.f); |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 bf.interf_angles_radians_[0]); | 240 bf.interf_angles_radians_[0]); |
| 138 EXPECT_FLOAT_EQ(M_PI / 2.f + bf.away_radians_, | 241 EXPECT_FLOAT_EQ(M_PI / 2.f + bf.away_radians_, |
| 139 bf.interf_angles_radians_[1]); | 242 bf.interf_angles_radians_[1]); |
| 140 bf.AimAt(AzimuthToSphericalPoint(bf.away_radians_ / 2.f)); | 243 bf.AimAt(AzimuthToSphericalPoint(bf.away_radians_ / 2.f)); |
| 141 EXPECT_EQ(2u, bf.interf_angles_radians_.size()); | 244 EXPECT_EQ(2u, bf.interf_angles_radians_.size()); |
| 142 EXPECT_FLOAT_EQ(-bf.away_radians_ / 2.f, bf.interf_angles_radians_[0]); | 245 EXPECT_FLOAT_EQ(-bf.away_radians_ / 2.f, bf.interf_angles_radians_[0]); |
| 143 EXPECT_FLOAT_EQ(3.f * bf.away_radians_ / 2.f, bf.interf_angles_radians_[1]); | 246 EXPECT_FLOAT_EQ(3.f * bf.away_radians_ / 2.f, bf.interf_angles_radians_[1]); |
| 144 } | 247 } |
| 145 } | 248 } |
| 146 | 249 |
| 250 // TODO(peah): Investigate why the nonlinear_beamformer.cc causes a DCHECK in |
| 251 // this setup. |
| 252 TEST(BeamformerBitExactnessTest, |
| 253 DISABLED_Stereo8kHz_ArrayGeometry1_TargetDirection1) { |
| 254 const float kOutputReference[] = {0.001318f, -0.001091f, 0.000990f, |
| 255 0.001318f, -0.001091f, 0.000990f}; |
| 256 |
| 257 RunBitExactnessTest(AudioProcessing::kSampleRate8kHz, CreateArrayGeometry(1), |
| 258 TargetDirection1, kOutputReference); |
| 259 } |
| 260 |
| 261 TEST(BeamformerBitExactnessTest, |
| 262 DISABLED_Stereo16kHz_ArrayGeometry1_TargetDirection1) { |
| 263 const float kOutputReference[] = {0.000064f, 0.000211f, 0.000075f, |
| 264 0.000064f, 0.000211f, 0.000075f}; |
| 265 |
| 266 RunBitExactnessTest(AudioProcessing::kSampleRate16kHz, CreateArrayGeometry(1), |
| 267 TargetDirection1, kOutputReference); |
| 268 } |
| 269 |
| 270 TEST(BeamformerBitExactnessTest, |
| 271 DISABLED_Stereo32kHz_ArrayGeometry1_TargetDirection1) { |
| 272 const float kOutputReference[] = {0.000183f, 0.000183f, 0.000183f, |
| 273 0.000183f, 0.000183f, 0.000183f}; |
| 274 |
| 275 RunBitExactnessTest(AudioProcessing::kSampleRate32kHz, CreateArrayGeometry(1), |
| 276 TargetDirection1, kOutputReference); |
| 277 } |
| 278 |
| 279 TEST(BeamformerBitExactnessTest, |
| 280 DISABLED_Stereo48kHz_ArrayGeometry1_TargetDirection1) { |
| 281 const float kOutputReference[] = {0.000155f, 0.000152f, 0.000159f, |
| 282 0.000155f, 0.000152f, 0.000159f}; |
| 283 |
| 284 RunBitExactnessTest(AudioProcessing::kSampleRate48kHz, CreateArrayGeometry(1), |
| 285 TargetDirection1, kOutputReference); |
| 286 } |
| 287 |
| 288 // TODO(peah): Investigate why the nonlinear_beamformer.cc causes a DCHECK in |
| 289 // this setup. |
| 290 TEST(BeamformerBitExactnessTest, |
| 291 DISABLED_Stereo8kHz_ArrayGeometry1_TargetDirection2) { |
| 292 const float kOutputReference[] = {0.001144f, -0.001026f, 0.001074f, |
| 293 -0.016205f, -0.007324f, -0.015656f}; |
| 294 |
| 295 RunBitExactnessTest(AudioProcessing::kSampleRate8kHz, CreateArrayGeometry(1), |
| 296 TargetDirection2, kOutputReference); |
| 297 } |
| 298 |
| 299 TEST(BeamformerBitExactnessTest, |
| 300 DISABLED_Stereo16kHz_ArrayGeometry1_TargetDirection2) { |
| 301 const float kOutputReference[] = {0.001144f, -0.001026f, 0.001074f, |
| 302 0.001144f, -0.001026f, 0.001074f}; |
| 303 |
| 304 RunBitExactnessTest(AudioProcessing::kSampleRate16kHz, CreateArrayGeometry(1), |
| 305 TargetDirection2, kOutputReference); |
| 306 } |
| 307 |
| 308 TEST(BeamformerBitExactnessTest, |
| 309 DISABLED_Stereo32kHz_ArrayGeometry1_TargetDirection2) { |
| 310 const float kOutputReference[] = {0.000732f, -0.000397f, 0.000610f, |
| 311 0.000732f, -0.000397f, 0.000610f}; |
| 312 |
| 313 RunBitExactnessTest(AudioProcessing::kSampleRate32kHz, CreateArrayGeometry(1), |
| 314 TargetDirection2, kOutputReference); |
| 315 } |
| 316 |
| 317 TEST(BeamformerBitExactnessTest, |
| 318 DISABLED_Stereo48kHz_ArrayGeometry1_TargetDirection2) { |
| 319 const float kOutputReference[] = {0.000106f, -0.000464f, 0.000188f, |
| 320 0.000106f, -0.000464f, 0.000188f}; |
| 321 |
| 322 RunBitExactnessTest(AudioProcessing::kSampleRate48kHz, CreateArrayGeometry(1), |
| 323 TargetDirection2, kOutputReference); |
| 324 } |
| 325 |
| 326 TEST(BeamformerBitExactnessTest, |
| 327 DISABLED_Stereo8kHz_ArrayGeometry2_TargetDirection2) { |
| 328 const float kOutputReference[] = {-0.000649f, 0.000576f, -0.000148f, |
| 329 -0.000649f, 0.000576f, -0.000148f}; |
| 330 |
| 331 RunBitExactnessTest(AudioProcessing::kSampleRate8kHz, CreateArrayGeometry(2), |
| 332 TargetDirection2, kOutputReference); |
| 333 } |
| 334 |
| 335 TEST(BeamformerBitExactnessTest, |
| 336 DISABLED_Stereo16kHz_ArrayGeometry2_TargetDirection2) { |
| 337 const float kOutputReference[] = {0.000808f, -0.000695f, 0.000739f, |
| 338 0.000808f, -0.000695f, 0.000739f}; |
| 339 |
| 340 RunBitExactnessTest(AudioProcessing::kSampleRate16kHz, CreateArrayGeometry(2), |
| 341 TargetDirection2, kOutputReference); |
| 342 } |
| 343 |
| 344 TEST(BeamformerBitExactnessTest, |
| 345 DISABLED_Stereo32kHz_ArrayGeometry2_TargetDirection2) { |
| 346 const float kOutputReference[] = {0.000580f, -0.000183f, 0.000458f, |
| 347 0.000580f, -0.000183f, 0.000458f}; |
| 348 |
| 349 RunBitExactnessTest(AudioProcessing::kSampleRate32kHz, CreateArrayGeometry(2), |
| 350 TargetDirection2, kOutputReference); |
| 351 } |
| 352 |
| 353 TEST(BeamformerBitExactnessTest, |
| 354 DISABLED_Stereo48kHz_ArrayGeometry2_TargetDirection2) { |
| 355 const float kOutputReference[] = {0.000075f, -0.000288f, 0.000156f, |
| 356 0.000075f, -0.000288f, 0.000156f}; |
| 357 |
| 358 RunBitExactnessTest(AudioProcessing::kSampleRate48kHz, CreateArrayGeometry(2), |
| 359 TargetDirection2, kOutputReference); |
| 360 } |
| 361 |
| 362 // TODO(peah): Investigate why the nonlinear_beamformer.cc causes a DCHECK in |
| 363 // this setup. |
| 364 TEST(BeamformerBitExactnessTest, |
| 365 DISABLED_Stereo16kHz_ArrayGeometry3_TargetDirection1) { |
| 366 const float kOutputReference[] = {-0.000161f, 0.000171f, -0.000096f, |
| 367 0.001007f, 0.000427f, 0.000977f}; |
| 368 |
| 369 RunBitExactnessTest(AudioProcessing::kSampleRate16kHz, CreateArrayGeometry(3), |
| 370 TargetDirection1, kOutputReference); |
| 371 } |
| 372 |
| 147 } // namespace webrtc | 373 } // namespace webrtc |
| OLD | NEW |