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:
@ -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_;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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 =
|
||||
|
||||
@ -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));
|
||||
});
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user