diff --git a/modules/audio_device/include/test_audio_device.cc b/modules/audio_device/include/test_audio_device.cc index fa4f3fe9d3..4c29c98f2c 100644 --- a/modules/audio_device/include/test_audio_device.cc +++ b/modules/audio_device/include/test_audio_device.cc @@ -294,6 +294,150 @@ class DiscardRenderer final : public TestAudioDeviceModule::Renderer { const int num_channels_; }; +class RawFileReader final : public TestAudioDeviceModule::Capturer { + public: + RawFileReader(absl::string_view input_file_name, + int sampling_frequency_in_hz, + int num_channels, + bool repeat) + : input_file_name_(input_file_name), + sampling_frequency_in_hz_(sampling_frequency_in_hz), + num_channels_(num_channels), + repeat_(repeat), + read_buffer_( + TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz) * + num_channels * 2, + 0) { + input_file_ = FileWrapper::OpenReadOnly(input_file_name_); + RTC_CHECK(input_file_.is_open()) + << "Failed to open audio input file: " << input_file_name_; + } + + ~RawFileReader() override { input_file_.Close(); } + + int SamplingFrequency() const override { return sampling_frequency_in_hz_; } + + int NumChannels() const override { return num_channels_; } + + bool Capture(rtc::BufferT* buffer) override { + buffer->SetData( + TestAudioDeviceModule::SamplesPerFrame(SamplingFrequency()) * + NumChannels(), + [&](rtc::ArrayView data) { + rtc::ArrayView read_buffer_view = ReadBufferView(); + size_t size = data.size() * 2; + size_t read = input_file_.Read(read_buffer_view.data(), size); + if (read < size && repeat_) { + do { + input_file_.Rewind(); + size_t delta = input_file_.Read( + read_buffer_view.subview(read).data(), size - read); + RTC_CHECK_GT(delta, 0) << "No new data to read from file"; + read += delta; + } while (read < size); + } + memcpy(data.data(), read_buffer_view.data(), size); + return read / 2; + }); + return buffer->size() > 0; + } + + private: + rtc::ArrayView ReadBufferView() { return read_buffer_; } + + const std::string input_file_name_; + const int sampling_frequency_in_hz_; + const int num_channels_; + const bool repeat_; + FileWrapper input_file_; + std::vector read_buffer_; +}; + +class RawFileWriter : public TestAudioDeviceModule::Renderer { + public: + RawFileWriter(absl::string_view output_file_name, + int sampling_frequency_in_hz, + int num_channels) + : output_file_name_(output_file_name), + sampling_frequency_in_hz_(sampling_frequency_in_hz), + num_channels_(num_channels), + silent_audio_( + TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz) * + num_channels * 2, + 0), + write_buffer_( + TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz) * + num_channels * 2, + 0), + started_writing_(false), + trailing_zeros_(0) { + output_file_ = FileWrapper::OpenWriteOnly(output_file_name_); + RTC_CHECK(output_file_.is_open()) + << "Failed to open playout file" << output_file_name_; + } + ~RawFileWriter() override { output_file_.Close(); } + + int SamplingFrequency() const override { return sampling_frequency_in_hz_; } + + int NumChannels() const override { return num_channels_; } + + bool Render(rtc::ArrayView data) override { + const int16_t kAmplitudeThreshold = 5; + + const int16_t* begin = data.begin(); + const int16_t* end = data.end(); + if (!started_writing_) { + // Cut off silence at the beginning. + while (begin < end) { + if (std::abs(*begin) > kAmplitudeThreshold) { + started_writing_ = true; + break; + } + ++begin; + } + } + if (started_writing_) { + // Cut off silence at the end. + while (begin < end) { + if (*(end - 1) != 0) { + break; + } + --end; + } + if (begin < end) { + // If it turns out that the silence was not final, need to write all the + // skipped zeros and continue writing audio. + while (trailing_zeros_ > 0) { + const size_t zeros_to_write = + std::min(trailing_zeros_, silent_audio_.size()); + output_file_.Write(silent_audio_.data(), zeros_to_write * 2); + trailing_zeros_ -= zeros_to_write; + } + WriteInt16(begin, end); + } + // Save the number of zeros we skipped in case this needs to be restored. + trailing_zeros_ += data.end() - end; + } + return true; + } + + private: + void WriteInt16(const int16_t* begin, const int16_t* end) { + int size = (end - begin) * sizeof(int16_t); + memcpy(write_buffer_.data(), begin, size); + output_file_.Write(write_buffer_.data(), size); + } + + const std::string output_file_name_; + const int sampling_frequency_in_hz_; + const int num_channels_; + FileWrapper output_file_; + std::vector silent_audio_; + std::vector write_buffer_; + bool started_writing_; + size_t trailing_zeros_; +}; + } // namespace size_t TestAudioDeviceModule::SamplesPerFrame(int sampling_frequency_in_hz) { @@ -376,4 +520,21 @@ TestAudioDeviceModule::CreateBoundedWavFileWriter(absl::string_view filename, filename, sampling_frequency_in_hz, num_channels); } +std::unique_ptr +TestAudioDeviceModule::CreateRawFileReader(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels, + bool repeat) { + return std::make_unique(filename, sampling_frequency_in_hz, + num_channels, repeat); +} + +std::unique_ptr +TestAudioDeviceModule::CreateRawFileWriter(absl::string_view filename, + int sampling_frequency_in_hz, + int num_channels) { + return std::make_unique(filename, sampling_frequency_in_hz, + num_channels); +} + } // namespace webrtc diff --git a/modules/audio_device/include/test_audio_device.h b/modules/audio_device/include/test_audio_device.h index 751eae6b7b..4b2d755ae1 100644 --- a/modules/audio_device/include/test_audio_device.h +++ b/modules/audio_device/include/test_audio_device.h @@ -102,8 +102,8 @@ class TestAudioDeviceModule { // WavReader and WavWriter creation based on file name. - // Returns a Capturer instance that gets its data from a file. The sample rate - // and channels will be checked against the Wav file. + // Returns a Capturer instance that gets its data from a WAV file. The sample + // rate and channels will be checked against the Wav file. static std::unique_ptr CreateWavFileReader( absl::string_view filename, int sampling_frequency_in_hz, @@ -131,6 +131,21 @@ class TestAudioDeviceModule { int sampling_frequency_in_hz, int num_channels = 1); + // Returns a Capturer instance that gets its data from a raw file (*.raw). + static std::unique_ptr CreateRawFileReader( + absl::string_view filename, + int sampling_frequency_in_hz = 48000, + int num_channels = 2, + bool repeat = true); + + // Returns a Renderer instance that writes its data to a raw file (*.raw), + // cutting off silence at the beginning (not necessarily perfect silence, see + // kAmplitudeThreshold) and at the end (only actual 0 samples in this case). + static std::unique_ptr CreateRawFileWriter( + absl::string_view filename, + int sampling_frequency_in_hz = 48000, + int num_channels = 2); + private: TestAudioDeviceModule() = default; }; diff --git a/modules/audio_device/include/test_audio_device_unittest.cc b/modules/audio_device/include/test_audio_device_unittest.cc index 54ede808b1..7a122ca84b 100644 --- a/modules/audio_device/include/test_audio_device_unittest.cc +++ b/modules/audio_device/include/test_audio_device_unittest.cc @@ -34,9 +34,8 @@ namespace webrtc { namespace { -void RunTest(const std::vector& input_samples, - const std::vector& expected_samples, - size_t samples_per_frame) { +void RunWavTest(const std::vector& input_samples, + const std::vector& expected_samples) { const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); @@ -79,7 +78,7 @@ TEST(BoundedWavFileWriterTest, NoSilence) { 75, 1234, 243, -1231, -22222, 0, 3, 88, 1222, -1213, -13222, -7, -3525, 5787, -25247, 8}; static const std::vector kExpectedSamples = kInputSamples; - RunTest(kInputSamples, kExpectedSamples, 8); + RunWavTest(kInputSamples, kExpectedSamples); } TEST(BoundedWavFileWriterTest, SomeStartSilence) { @@ -87,7 +86,7 @@ TEST(BoundedWavFileWriterTest, SomeStartSilence) { 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; static const std::vector kExpectedSamples(kInputSamples.begin() + 10, kInputSamples.end()); - RunTest(kInputSamples, kExpectedSamples, 8); + RunWavTest(kInputSamples, kExpectedSamples); } TEST(BoundedWavFileWriterTest, NegativeStartSilence) { @@ -95,7 +94,7 @@ TEST(BoundedWavFileWriterTest, NegativeStartSilence) { 0, -4, -6, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; static const std::vector kExpectedSamples(kInputSamples.begin() + 2, kInputSamples.end()); - RunTest(kInputSamples, kExpectedSamples, 8); + RunWavTest(kInputSamples, kExpectedSamples); } TEST(BoundedWavFileWriterTest, SomeEndSilence) { @@ -103,7 +102,7 @@ TEST(BoundedWavFileWriterTest, SomeEndSilence) { 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static const std::vector kExpectedSamples(kInputSamples.begin(), kInputSamples.end() - 9); - RunTest(kInputSamples, kExpectedSamples, 8); + RunWavTest(kInputSamples, kExpectedSamples); } TEST(BoundedWavFileWriterTest, DoubleEndSilence) { @@ -112,7 +111,7 @@ TEST(BoundedWavFileWriterTest, DoubleEndSilence) { 0, -1213, -13222, -7, -3525, 5787, 0, 0}; static const std::vector kExpectedSamples(kInputSamples.begin(), kInputSamples.end() - 2); - RunTest(kInputSamples, kExpectedSamples, 8); + RunWavTest(kInputSamples, kExpectedSamples); } TEST(BoundedWavFileWriterTest, DoubleSilence) { @@ -120,7 +119,7 @@ TEST(BoundedWavFileWriterTest, DoubleSilence) { -3525, 5787, 0, 0}; static const std::vector kExpectedSamples(kInputSamples.begin() + 1, kInputSamples.end() - 2); - RunTest(kInputSamples, kExpectedSamples, 8); + RunWavTest(kInputSamples, kExpectedSamples); } TEST(BoundedWavFileWriterTest, EndSilenceCutoff) { @@ -128,7 +127,7 @@ TEST(BoundedWavFileWriterTest, EndSilenceCutoff) { 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0}; static const std::vector kExpectedSamples(kInputSamples.begin(), kInputSamples.end() - 4); - RunTest(kInputSamples, kExpectedSamples, 8); + RunWavTest(kInputSamples, kExpectedSamples); } TEST(WavFileReaderTest, RepeatedTrueWithSingleFrameFileReadTwice) { @@ -146,7 +145,7 @@ TEST(WavFileReaderTest, RepeatedTrueWithSingleFrameFileReadTwice) { EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), kSamplesPerFrame); - // Create wav file to read. + // Create raw file to read. { std::unique_ptr writer = TestAudioDeviceModule::CreateWavFileWriter(output_filename, 800); @@ -171,6 +170,154 @@ TEST(WavFileReaderTest, RepeatedTrueWithSingleFrameFileReadTwice) { remove(output_filename.c_str()); } +void RunRawTestNoRepeat(const std::vector& input_samples, + const std::vector& expected_samples) { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + const std::string output_filename = test::OutputPath() + "RawFileTest_" + + test_info->name() + "_" + + std::to_string(std::rand()) + ".raw"; + + static const size_t kSamplesPerFrame = 8; + static const int kSampleRate = kSamplesPerFrame * 100; + EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), + kSamplesPerFrame); + + // Test through file name API. + { + std::unique_ptr writer = + TestAudioDeviceModule::CreateRawFileWriter( + output_filename, /*sampling_frequency_in_hz=*/800); + + for (size_t i = 0; i < input_samples.size(); i += kSamplesPerFrame) { + EXPECT_TRUE(writer->Render(rtc::ArrayView( + &input_samples[i], + std::min(kSamplesPerFrame, input_samples.size() - i)))); + } + } + + { + std::unique_ptr reader = + TestAudioDeviceModule::CreateRawFileReader( + output_filename, /*sampling_frequency_in_hz=*/800, + /*num_channels=*/2, /*repeat=*/false); + rtc::BufferT buffer(expected_samples.size()); + rtc::BufferT expected_buffer(expected_samples.size()); + expected_buffer.SetData(expected_samples); + EXPECT_TRUE(reader->Capture(&buffer)); + EXPECT_EQ(expected_buffer, buffer); + EXPECT_FALSE(reader->Capture(&buffer)); + EXPECT_TRUE(buffer.empty()); + } + + remove(output_filename.c_str()); +} + +TEST(RawFileWriterTest, NoSilence) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 3, 88, + 1222, -1213, -13222, -7, -3525, 5787, -25247, 8}; + static const std::vector kExpectedSamples = kInputSamples; + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, SomeStartSilence) { + static const std::vector kInputSamples = { + 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; + static const std::vector kExpectedSamples(kInputSamples.begin() + 10, + kInputSamples.end()); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, NegativeStartSilence) { + static const std::vector kInputSamples = { + 0, -4, -6, 0, 3, 0, 0, 0, 0, 3, -13222, -7, -3525, 5787, -25247, 8}; + static const std::vector kExpectedSamples(kInputSamples.begin() + 2, + kInputSamples.end()); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, SomeEndSilence) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin(), + kInputSamples.end() - 9); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, DoubleEndSilence) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 0, 0, + 0, -1213, -13222, -7, -3525, 5787, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin(), + kInputSamples.end() - 2); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, DoubleSilence) { + static const std::vector kInputSamples = {0, -1213, -13222, -7, + -3525, 5787, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin() + 1, + kInputSamples.end() - 2); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, EndSilenceCutoff) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 1, 0, 0, 0, 0}; + static const std::vector kExpectedSamples(kInputSamples.begin(), + kInputSamples.end() - 4); + RunRawTestNoRepeat(kInputSamples, kExpectedSamples); +} + +TEST(RawFileWriterTest, Repeat) { + static const std::vector kInputSamples = { + 75, 1234, 243, -1231, -22222, 0, 3, 88, + 1222, -1213, -13222, -7, -3525, 5787, -25247, 8}; + static const rtc::BufferT kExpectedSamples(kInputSamples.data(), + kInputSamples.size()); + + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + + const std::string output_filename = test::OutputPath() + "RawFileTest_" + + test_info->name() + "_" + + std::to_string(std::rand()) + ".raw"; + + static const size_t kSamplesPerFrame = 8; + static const int kSampleRate = kSamplesPerFrame * 100; + EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate), + kSamplesPerFrame); + + // Test through file name API. + { + std::unique_ptr writer = + TestAudioDeviceModule::CreateRawFileWriter( + output_filename, /*sampling_frequency_in_hz=*/800); + + for (size_t i = 0; i < kInputSamples.size(); i += kSamplesPerFrame) { + EXPECT_TRUE(writer->Render(rtc::ArrayView( + &kInputSamples[i], + std::min(kSamplesPerFrame, kInputSamples.size() - i)))); + } + } + + { + std::unique_ptr reader = + TestAudioDeviceModule::CreateRawFileReader( + output_filename, /*sampling_frequency_in_hz=*/800, + /*num_channels=*/2, /*repeat=*/true); + rtc::BufferT buffer(kExpectedSamples.size()); + EXPECT_TRUE(reader->Capture(&buffer)); + EXPECT_EQ(kExpectedSamples, buffer); + EXPECT_TRUE(reader->Capture(&buffer)); + EXPECT_EQ(kExpectedSamples, buffer); + } + + remove(output_filename.c_str()); +} + TEST(PulsedNoiseCapturerTest, SetMaxAmplitude) { const int16_t kAmplitude = 50; std::unique_ptr capturer =