Add ability to play audio in circle for TestAudioDevice wav file capturer

Also use this ability in PC smoke test.

Bug: webrtc:10138
Change-Id: I83d526344f203082a19377d9642c9e453454f7ad
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/133163
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#27649}
This commit is contained in:
Artem Titov
2019-04-16 16:49:32 +02:00
committed by Commit Bot
parent 50150a18f4
commit 153056b059
8 changed files with 174 additions and 17 deletions

View File

@ -74,12 +74,20 @@ WavReader::WavReader(rtc::PlatformFile file) {
num_samples_remaining_ = num_samples_;
RTC_CHECK_EQ(kWavFormat, format);
RTC_CHECK_EQ(kBytesPerSample, bytes_per_sample);
RTC_CHECK_EQ(0, fgetpos(file_handle_, &data_start_pos_))
<< "Failed to get WAV data position from file";
}
WavReader::~WavReader() {
Close();
}
void WavReader::Reset() {
RTC_CHECK_EQ(0, fsetpos(file_handle_, &data_start_pos_))
<< "Failed to set position in the file to WAV data start position";
num_samples_remaining_ = num_samples_;
}
int WavReader::sample_rate() const {
return sample_rate_;
}

View File

@ -77,6 +77,9 @@ class WavReader final : public WavFile {
// Close the WAV file.
~WavReader() override;
// Resets position to the beginning of the file.
void Reset();
// Returns the number of samples read. If this is less than requested,
// verifies that the end of the file was reached.
size_t ReadSamples(size_t num_samples, float* samples);
@ -93,6 +96,7 @@ class WavReader final : public WavFile {
size_t num_samples_; // Total number of samples in the file.
size_t num_samples_remaining_;
FILE* file_handle_; // Input file, owned by this class.
fpos_t data_start_pos_; // Position in the file immediately after WAV header.
RTC_DISALLOW_COPY_AND_ASSIGN(WavReader);
};

View File

@ -24,9 +24,11 @@
#if defined(WEBRTC_MAC)
#define MAYBE_CPP DISABLED_CPP
#define MAYBE_CPPFileDescriptor DISABLED_CPPFileDescriptor
#define MAYBE_CPPReset DISABLED_CPPReset
#else
#define MAYBE_CPP CPP
#define MAYBE_CPPFileDescriptor CPPFileDescriptor
#define MAYBE_CPPReset CPPReset
#endif
namespace webrtc {
@ -259,4 +261,77 @@ TEST(WavWriterTest, MAYBE_CPPFileDescriptor) {
}
}
// Write a tiny WAV file with the C++ interface then read-reset-read.
TEST(WavReaderTest, MAYBE_CPPReset) {
const std::string outfile = test::OutputPath() + "wavtest4.wav";
static const size_t kNumSamples = 3;
{
WavWriter w(outfile, 14099, 1);
EXPECT_EQ(14099, w.sample_rate());
EXPECT_EQ(1u, w.num_channels());
EXPECT_EQ(0u, w.num_samples());
w.WriteSamples(kSamples, kNumSamples);
EXPECT_EQ(kNumSamples, w.num_samples());
}
// Write some extra "metadata" to the file that should be silently ignored
// by WavReader. We don't use WavWriter directly for this because it doesn't
// support metadata.
static const uint8_t kMetadata[] = {101, 202};
{
FILE* f = fopen(outfile.c_str(), "ab");
ASSERT_TRUE(f);
ASSERT_EQ(1u, fwrite(kMetadata, sizeof(kMetadata), 1, f));
fclose(f);
}
static const uint8_t kExpectedContents[] = {
// clang-format off
// clang formatting doesn't respect inline comments.
'R', 'I', 'F', 'F',
42, 0, 0, 0, // size of whole file - 8: 6 + 44 - 8
'W', 'A', 'V', 'E',
'f', 'm', 't', ' ',
16, 0, 0, 0, // size of fmt block - 8: 24 - 8
1, 0, // format: PCM (1)
1, 0, // channels: 1
0x13, 0x37, 0, 0, // sample rate: 14099
0x26, 0x6e, 0, 0, // byte rate: 2 * 14099
2, 0, // block align: NumChannels * BytesPerSample
16, 0, // bits per sample: 2 * 8
'd', 'a', 't', 'a',
6, 0, 0, 0, // size of payload: 6
0, 0, // first sample: 0.0
10, 0, // second sample: 10.0
0xff, 0x7f, // third sample: 4e4 (saturated)
kMetadata[0], kMetadata[1],
// clang-format on
};
static const size_t kContentSize =
kWavHeaderSize + kNumSamples * sizeof(int16_t) + sizeof(kMetadata);
static_assert(sizeof(kExpectedContents) == kContentSize, "content size");
EXPECT_EQ(kContentSize, test::GetFileSize(outfile));
FILE* f = fopen(outfile.c_str(), "rb");
ASSERT_TRUE(f);
uint8_t contents[kContentSize];
ASSERT_EQ(1u, fread(contents, kContentSize, 1, f));
EXPECT_EQ(0, fclose(f));
EXPECT_EQ(0, memcmp(kExpectedContents, contents, kContentSize));
{
WavReader r(outfile);
EXPECT_EQ(14099, r.sample_rate());
EXPECT_EQ(1u, r.num_channels());
EXPECT_EQ(kNumSamples, r.num_samples());
static const float kTruncatedSamples[] = {0.0, 10.0, 32767.0};
float samples[kNumSamples];
EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
r.Reset();
EXPECT_EQ(kNumSamples, r.ReadSamples(kNumSamples, samples));
EXPECT_EQ(0, memcmp(kTruncatedSamples, samples, sizeof(samples)));
EXPECT_EQ(0u, r.ReadSamples(kNumSamples, samples));
}
}
} // namespace webrtc

View File

@ -269,17 +269,21 @@ class WavFileReader final : public TestAudioDeviceModule::Capturer {
public:
WavFileReader(std::string filename,
int sampling_frequency_in_hz,
int num_channels)
int num_channels,
bool repeat)
: WavFileReader(absl::make_unique<WavReader>(filename),
sampling_frequency_in_hz,
num_channels) {}
num_channels,
repeat) {}
WavFileReader(rtc::PlatformFile file,
int sampling_frequency_in_hz,
int num_channels)
int num_channels,
bool repeat)
: WavFileReader(absl::make_unique<WavReader>(file),
sampling_frequency_in_hz,
num_channels) {}
num_channels,
repeat) {}
int SamplingFrequency() const override { return sampling_frequency_in_hz_; }
@ -290,7 +294,17 @@ class WavFileReader final : public TestAudioDeviceModule::Capturer {
TestAudioDeviceModule::SamplesPerFrame(sampling_frequency_in_hz_) *
num_channels_,
[&](rtc::ArrayView<int16_t> data) {
return wav_reader_->ReadSamples(data.size(), data.data());
size_t read = wav_reader_->ReadSamples(data.size(), data.data());
if (read < data.size() && repeat_) {
do {
wav_reader_->Reset();
size_t delta = wav_reader_->ReadSamples(
data.size() - read, data.subview(read).data());
RTC_CHECK_GT(delta, 0) << "No new data read from file";
read += delta;
} while (read < data.size());
}
return read;
});
return buffer->size() > 0;
}
@ -298,17 +312,20 @@ class WavFileReader final : public TestAudioDeviceModule::Capturer {
private:
WavFileReader(std::unique_ptr<WavReader> wav_reader,
int sampling_frequency_in_hz,
int num_channels)
int num_channels,
bool repeat)
: sampling_frequency_in_hz_(sampling_frequency_in_hz),
num_channels_(num_channels),
wav_reader_(std::move(wav_reader)) {
wav_reader_(std::move(wav_reader)),
repeat_(repeat) {
RTC_CHECK_EQ(wav_reader_->sample_rate(), sampling_frequency_in_hz);
RTC_CHECK_EQ(wav_reader_->num_channels(), num_channels);
}
int sampling_frequency_in_hz_;
const int sampling_frequency_in_hz_;
const int num_channels_;
std::unique_ptr<WavReader> wav_reader_;
const bool repeat_;
};
class WavFileWriter final : public TestAudioDeviceModule::Renderer {
@ -495,16 +512,16 @@ TestAudioDeviceModule::CreateWavFileReader(std::string filename,
int sampling_frequency_in_hz,
int num_channels) {
return absl::make_unique<WavFileReader>(filename, sampling_frequency_in_hz,
num_channels);
num_channels, false);
}
std::unique_ptr<TestAudioDeviceModule::Capturer>
TestAudioDeviceModule::CreateWavFileReader(std::string filename) {
TestAudioDeviceModule::CreateWavFileReader(std::string filename, bool repeat) {
WavReader reader(filename);
int sampling_frequency_in_hz = reader.sample_rate();
int num_channels = rtc::checked_cast<int>(reader.num_channels());
return absl::make_unique<WavFileReader>(filename, sampling_frequency_in_hz,
num_channels);
num_channels, repeat);
}
std::unique_ptr<TestAudioDeviceModule::Renderer>
@ -528,16 +545,17 @@ TestAudioDeviceModule::CreateWavFileReader(rtc::PlatformFile file,
int sampling_frequency_in_hz,
int num_channels) {
return absl::make_unique<WavFileReader>(file, sampling_frequency_in_hz,
num_channels);
num_channels, false);
}
std::unique_ptr<TestAudioDeviceModule::Capturer>
TestAudioDeviceModule::CreateWavFileReader(rtc::PlatformFile file) {
TestAudioDeviceModule::CreateWavFileReader(rtc::PlatformFile file,
bool repeat) {
WavReader reader(file);
int sampling_frequency_in_hz = reader.sample_rate();
int num_channels = rtc::checked_cast<int>(reader.num_channels());
return absl::make_unique<WavFileReader>(file, sampling_frequency_in_hz,
num_channels);
num_channels, repeat);
}
std::unique_ptr<TestAudioDeviceModule::Renderer>

View File

@ -113,7 +113,10 @@ class TestAudioDeviceModule : public AudioDeviceModule {
// Returns a Capturer instance that gets its data from a file.
// Automatically detects sample rate and num of channels.
static std::unique_ptr<Capturer> CreateWavFileReader(std::string filename);
// |repeat| - if true, the file will be replayed from the start when we reach
// the end of file.
static std::unique_ptr<Capturer> CreateWavFileReader(std::string filename,
bool repeat = false);
// Returns a Renderer instance that writes its data to a file.
static std::unique_ptr<Renderer> CreateWavFileWriter(
@ -140,7 +143,10 @@ class TestAudioDeviceModule : public AudioDeviceModule {
// Returns a Capturer instance that gets its data from a file.
// Automatically detects sample rate and num of channels.
static std::unique_ptr<Capturer> CreateWavFileReader(rtc::PlatformFile file);
// |repeat| - if true, the file will be replayed from the start when we reach
// the end of file.
static std::unique_ptr<Capturer> CreateWavFileReader(rtc::PlatformFile file,
bool repeat = false);
// Returns a Renderer instance that writes its data to a file.
static std::unique_ptr<Renderer> CreateWavFileWriter(

View File

@ -173,6 +173,46 @@ TEST(BoundedWavFileWriterTest, EndSilenceCutoff) {
RunTest(kInputSamples, kExpectedSamples, 8);
}
TEST(WavFileReaderTest, RepeatedTrueWithSingleFrameFileReadTwice) {
static const std::vector<int16_t> kInputSamples = {75, 1234, 243, -1231,
-22222, 0, 3, 88};
static const rtc::BufferT<int16_t> kExpectedSamples(kInputSamples.data(),
kInputSamples.size());
const std::string output_filename = test::OutputPath() +
"WavFileReaderTest_RepeatedTrue_" +
std::to_string(std::rand()) + ".wav";
static const size_t kSamplesPerFrame = 8;
static const int kSampleRate = kSamplesPerFrame * 100;
EXPECT_EQ(TestAudioDeviceModule::SamplesPerFrame(kSampleRate),
kSamplesPerFrame);
// Create wav file to read.
{
std::unique_ptr<TestAudioDeviceModule::Renderer> writer =
TestAudioDeviceModule::CreateWavFileWriter(output_filename, 800);
for (size_t i = 0; i < kInputSamples.size(); i += kSamplesPerFrame) {
EXPECT_TRUE(writer->Render(rtc::ArrayView<const int16_t>(
&kInputSamples[i],
std::min(kSamplesPerFrame, kInputSamples.size() - i))));
}
}
{
std::unique_ptr<TestAudioDeviceModule::Capturer> reader =
TestAudioDeviceModule::CreateWavFileReader(output_filename, true);
rtc::BufferT<int16_t> 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<TestAudioDeviceModule::PulsedNoiseCapturer> capturer =

View File

@ -85,6 +85,9 @@ TEST(PeerConnectionE2EQualityTestSmokeTest, RunWithEmulatedNetwork) {
alice->AddVideoConfig(std::move(video_config));
AudioConfig audio_config;
audio_config.stream_label = "alice-audio";
audio_config.mode = AudioConfig::Mode::kFile;
audio_config.input_file_name = test::ResourcePath(
"pc_quality_smoke_test_alice_source", "wav");
alice->SetAudioConfig(std::move(audio_config));
});
@ -98,6 +101,9 @@ TEST(PeerConnectionE2EQualityTestSmokeTest, RunWithEmulatedNetwork) {
bob->AddVideoConfig(std::move(video_config));
AudioConfig audio_config;
audio_config.stream_label = "bob-audio";
audio_config.mode = AudioConfig::Mode::kFile;
audio_config.input_file_name = test::ResourcePath(
"pc_quality_smoke_test_bob_source", "wav");
bob->SetAudioConfig(std::move(audio_config));
});

View File

@ -112,7 +112,7 @@ struct TestPeerComponents {
if (audio_config.mode == AudioConfig::Mode::kFile) {
RTC_DCHECK(audio_config.input_file_name);
return TestAudioDeviceModule::CreateWavFileReader(
audio_config.input_file_name.value());
audio_config.input_file_name.value(), /*repeat=*/true);
}
RTC_NOTREACHED() << "Unknown audio_config->mode";
return nullptr;