diff --git a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_unittest.cc b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_unittest.cc index a38a49b1e1..552422ce3a 100644 --- a/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_unittest.cc +++ b/webrtc/modules/audio_processing/beamformer/nonlinear_beamformer_unittest.cc @@ -16,6 +16,10 @@ #include #include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/array_view.h" +#include "webrtc/modules/audio_processing/audio_buffer.h" +#include "webrtc/modules/audio_processing/test/audio_buffer_tools.h" +#include "webrtc/modules/audio_processing/test/bitexactness_tools.h" namespace webrtc { namespace { @@ -48,6 +52,105 @@ void AimAndVerify(NonlinearBeamformer* bf, float target_azimuth_radians) { Verify(bf, target_azimuth_radians); } +// Bitexactness test code. +const size_t kNumFramesToProcess = 1000; + +void ProcessOneFrame(int sample_rate_hz, + AudioBuffer* capture_audio_buffer, + Beamformer* beamformer) { + if (sample_rate_hz > AudioProcessing::kSampleRate16kHz) { + capture_audio_buffer->SplitIntoFrequencyBands(); + } + + beamformer->ProcessChunk(*capture_audio_buffer->split_data_f(), + capture_audio_buffer->split_data_f()); + capture_audio_buffer->set_num_channels(1); + + if (sample_rate_hz > AudioProcessing::kSampleRate16kHz) { + capture_audio_buffer->MergeFrequencyBands(); + } +} + +int BeamformerSampleRate(int sample_rate_hz) { + return (sample_rate_hz > AudioProcessing::kSampleRate16kHz + ? AudioProcessing::kSampleRate16kHz + : sample_rate_hz); +} + +void RunBitExactnessTest(int sample_rate_hz, + const std::vector& array_geometry, + const SphericalPointf& target_direction, + rtc::ArrayView output_reference) { + NonlinearBeamformer beamformer(array_geometry, target_direction); + beamformer.Initialize(AudioProcessing::kChunkSizeMs, + BeamformerSampleRate(sample_rate_hz)); + + const StreamConfig capture_config(sample_rate_hz, array_geometry.size(), + false); + AudioBuffer capture_buffer( + capture_config.num_frames(), capture_config.num_channels(), + capture_config.num_frames(), capture_config.num_channels(), + capture_config.num_frames()); + test::InputAudioFile capture_file( + test::GetApmCaptureTestVectorFileName(sample_rate_hz)); + std::vector capture_input(capture_config.num_frames() * + capture_config.num_channels()); + for (size_t frame_no = 0u; frame_no < kNumFramesToProcess; ++frame_no) { + ReadFloatSamplesFromStereoFile(capture_config.num_frames(), + capture_config.num_channels(), &capture_file, + capture_input); + + test::CopyVectorToAudioBuffer(capture_config, capture_input, + &capture_buffer); + + ProcessOneFrame(sample_rate_hz, &capture_buffer, &beamformer); + } + + // Extract and verify the test results. + std::vector capture_output; + test::ExtractVectorFromAudioBuffer(capture_config, &capture_buffer, + &capture_output); + + const float kTolerance = 1.f / static_cast(1 << 15); + + // Compare the output with the reference. Only the first values of the output + // from last frame processed are compared in order not having to specify all + // preceeding frames as testvectors. As the algorithm being tested has a + // memory, testing only the last frame implicitly also tests the preceeding + // frames. + EXPECT_TRUE(test::BitExactFrame( + capture_config.num_frames(), capture_config.num_channels(), + output_reference, capture_output, kTolerance)); +} + +std::vector CreateArrayGeometry(int variant) { + std::vector array_geometry; + switch (variant) { + case 1: + array_geometry.push_back(Point(-0.025f, 0.f, 0.f)); + array_geometry.push_back(Point(0.025f, 0.f, 0.f)); + break; + case 2: + array_geometry.push_back(Point(-0.035f, 0.f, 0.f)); + array_geometry.push_back(Point(0.035f, 0.f, 0.f)); + break; + case 3: + array_geometry.push_back(Point(-0.5f, 0.f, 0.f)); + array_geometry.push_back(Point(0.5f, 0.f, 0.f)); + break; + default: + RTC_CHECK(false); + } + return array_geometry; +} + +const SphericalPointf TargetDirection1(0.4f * static_cast(M_PI) / 2.f, + 0.f, + 1.f); +const SphericalPointf TargetDirection2(static_cast(M_PI) / 2.f, + 1.f, + 2.f); + } // namespace TEST(NonlinearBeamformerTest, AimingModifiesBeam) { @@ -144,4 +247,127 @@ TEST(NonlinearBeamformerTest, InterfAnglesTakeAmbiguityIntoAccount) { } } +// TODO(peah): Investigate why the nonlinear_beamformer.cc causes a DCHECK in +// this setup. +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo8kHz_ArrayGeometry1_TargetDirection1) { + const float kOutputReference[] = {0.001318f, -0.001091f, 0.000990f, + 0.001318f, -0.001091f, 0.000990f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate8kHz, CreateArrayGeometry(1), + TargetDirection1, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo16kHz_ArrayGeometry1_TargetDirection1) { + const float kOutputReference[] = {0.000064f, 0.000211f, 0.000075f, + 0.000064f, 0.000211f, 0.000075f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate16kHz, CreateArrayGeometry(1), + TargetDirection1, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo32kHz_ArrayGeometry1_TargetDirection1) { + const float kOutputReference[] = {0.000183f, 0.000183f, 0.000183f, + 0.000183f, 0.000183f, 0.000183f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate32kHz, CreateArrayGeometry(1), + TargetDirection1, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo48kHz_ArrayGeometry1_TargetDirection1) { + const float kOutputReference[] = {0.000155f, 0.000152f, 0.000159f, + 0.000155f, 0.000152f, 0.000159f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate48kHz, CreateArrayGeometry(1), + TargetDirection1, kOutputReference); +} + +// TODO(peah): Investigate why the nonlinear_beamformer.cc causes a DCHECK in +// this setup. +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo8kHz_ArrayGeometry1_TargetDirection2) { + const float kOutputReference[] = {0.001144f, -0.001026f, 0.001074f, + -0.016205f, -0.007324f, -0.015656f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate8kHz, CreateArrayGeometry(1), + TargetDirection2, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo16kHz_ArrayGeometry1_TargetDirection2) { + const float kOutputReference[] = {0.001144f, -0.001026f, 0.001074f, + 0.001144f, -0.001026f, 0.001074f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate16kHz, CreateArrayGeometry(1), + TargetDirection2, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo32kHz_ArrayGeometry1_TargetDirection2) { + const float kOutputReference[] = {0.000732f, -0.000397f, 0.000610f, + 0.000732f, -0.000397f, 0.000610f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate32kHz, CreateArrayGeometry(1), + TargetDirection2, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo48kHz_ArrayGeometry1_TargetDirection2) { + const float kOutputReference[] = {0.000106f, -0.000464f, 0.000188f, + 0.000106f, -0.000464f, 0.000188f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate48kHz, CreateArrayGeometry(1), + TargetDirection2, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo8kHz_ArrayGeometry2_TargetDirection2) { + const float kOutputReference[] = {-0.000649f, 0.000576f, -0.000148f, + -0.000649f, 0.000576f, -0.000148f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate8kHz, CreateArrayGeometry(2), + TargetDirection2, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo16kHz_ArrayGeometry2_TargetDirection2) { + const float kOutputReference[] = {0.000808f, -0.000695f, 0.000739f, + 0.000808f, -0.000695f, 0.000739f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate16kHz, CreateArrayGeometry(2), + TargetDirection2, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo32kHz_ArrayGeometry2_TargetDirection2) { + const float kOutputReference[] = {0.000580f, -0.000183f, 0.000458f, + 0.000580f, -0.000183f, 0.000458f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate32kHz, CreateArrayGeometry(2), + TargetDirection2, kOutputReference); +} + +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo48kHz_ArrayGeometry2_TargetDirection2) { + const float kOutputReference[] = {0.000075f, -0.000288f, 0.000156f, + 0.000075f, -0.000288f, 0.000156f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate48kHz, CreateArrayGeometry(2), + TargetDirection2, kOutputReference); +} + +// TODO(peah): Investigate why the nonlinear_beamformer.cc causes a DCHECK in +// this setup. +TEST(BeamformerBitExactnessTest, + DISABLED_Stereo16kHz_ArrayGeometry3_TargetDirection1) { + const float kOutputReference[] = {-0.000161f, 0.000171f, -0.000096f, + 0.001007f, 0.000427f, 0.000977f}; + + RunBitExactnessTest(AudioProcessing::kSampleRate16kHz, CreateArrayGeometry(3), + TargetDirection1, kOutputReference); +} + } // namespace webrtc