audioproc_f: input AEC dump as string, output audio to vector

This CL adds the following options:

pass an input AEC dump as a string (currently, the tool can only accept a path to an AEC dump file)
write the processed capture samples to a given vector

Bug: webrtc:10808
Change-Id: I02863c97ec3cd8c03ade2ea8521836f2e7417050
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/145208
Commit-Queue: Sonia-Florina Horchidan <soniahorchidan@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Per Åhgren <peah@webrtc.org>
Reviewed-by: Alessio Bazzica <alessiob@webrtc.org>
Reviewed-by: Ivo Creusen <ivoc@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28826}
This commit is contained in:
Sonia-Florina Horchidan
2019-08-12 09:57:01 +02:00
committed by Commit Bot
parent 81df62b456
commit b75d14c802
13 changed files with 201 additions and 51 deletions

View File

@ -20,7 +20,18 @@ namespace test {
int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[]) {
return AudioprocFloatImpl(std::move(ap_builder), argc, argv);
return AudioprocFloatImpl(std::move(ap_builder), argc, argv,
/*input_aecdump=*/"",
/*processed_capture_samples=*/nullptr);
}
int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[],
absl::string_view input_aecdump,
std::vector<float>* processed_capture_samples) {
return AudioprocFloatImpl(std::move(ap_builder), argc, argv, input_aecdump,
processed_capture_samples);
}
} // namespace test

View File

@ -12,6 +12,7 @@
#define API_TEST_AUDIOPROC_FLOAT_H_
#include <memory>
#include <vector>
#include "modules/audio_processing/include/audio_processing.h"
@ -36,6 +37,18 @@ int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[]);
// Interface for the audio processing simulation utility, which is similar to
// the one above, but which adds the option of receiving the input as a string
// and returning the output as an array. The first three arguments fulfill the
// same purpose as above. Pass the |input_aecdump| to provide the content of an
// AEC dump file as a string. After the simulation is completed,
// |processed_capture_samples| will contain the the samples processed on the
// capture side.
int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[],
absl::string_view input_aecdump,
std::vector<float>* processed_capture_samples);
} // namespace test
} // namespace webrtc

View File

@ -755,6 +755,7 @@ if (rtc_include_tests) {
"../../rtc_base:protobuf_utils",
"../../rtc_base:rtc_base_approved",
"../../rtc_base/system:arch",
"//third_party/abseil-cpp/absl/memory:memory",
]
}

View File

@ -212,8 +212,6 @@ void AecDumpBasedSimulator::PrepareReverseProcessStreamCall(
void AecDumpBasedSimulator::Process() {
CreateAudioProcessor();
dump_input_file_ = OpenFile(settings_.aec_dump_input_filename->c_str(), "rb");
if (settings_.artificial_nearend_filename) {
std::unique_ptr<WavReader> artificial_nearend_file(
new WavReader(settings_.artificial_nearend_filename->c_str()));
@ -231,39 +229,52 @@ void AecDumpBasedSimulator::Process() {
webrtc::audioproc::Event event_msg;
int num_forward_chunks_processed = 0;
while (ReadMessageFromFile(dump_input_file_, &event_msg)) {
switch (event_msg.type()) {
case webrtc::audioproc::Event::INIT:
RTC_CHECK(event_msg.has_init());
HandleMessage(event_msg.init());
break;
case webrtc::audioproc::Event::STREAM:
RTC_CHECK(event_msg.has_stream());
HandleMessage(event_msg.stream());
++num_forward_chunks_processed;
break;
case webrtc::audioproc::Event::REVERSE_STREAM:
RTC_CHECK(event_msg.has_reverse_stream());
HandleMessage(event_msg.reverse_stream());
break;
case webrtc::audioproc::Event::CONFIG:
RTC_CHECK(event_msg.has_config());
HandleMessage(event_msg.config());
break;
case webrtc::audioproc::Event::RUNTIME_SETTING:
HandleMessage(event_msg.runtime_setting());
break;
case webrtc::audioproc::Event::UNKNOWN_EVENT:
RTC_CHECK(false);
break;
}
if (settings_.aec_dump_input_string.has_value()) {
std::stringstream input;
input << settings_.aec_dump_input_string.value();
while (ReadMessageFromString(&input, &event_msg))
HandleEvent(event_msg, &num_forward_chunks_processed);
} else {
dump_input_file_ =
OpenFile(settings_.aec_dump_input_filename->c_str(), "rb");
while (ReadMessageFromFile(dump_input_file_, &event_msg))
HandleEvent(event_msg, &num_forward_chunks_processed);
fclose(dump_input_file_);
}
fclose(dump_input_file_);
DestroyAudioProcessor();
}
void AecDumpBasedSimulator::HandleEvent(
const webrtc::audioproc::Event& event_msg,
int* num_forward_chunks_processed) {
switch (event_msg.type()) {
case webrtc::audioproc::Event::INIT:
RTC_CHECK(event_msg.has_init());
HandleMessage(event_msg.init());
break;
case webrtc::audioproc::Event::STREAM:
RTC_CHECK(event_msg.has_stream());
HandleMessage(event_msg.stream());
++num_forward_chunks_processed;
break;
case webrtc::audioproc::Event::REVERSE_STREAM:
RTC_CHECK(event_msg.has_reverse_stream());
HandleMessage(event_msg.reverse_stream());
break;
case webrtc::audioproc::Event::CONFIG:
RTC_CHECK(event_msg.has_config());
HandleMessage(event_msg.config());
break;
case webrtc::audioproc::Event::RUNTIME_SETTING:
HandleMessage(event_msg.runtime_setting());
break;
case webrtc::audioproc::Event::UNKNOWN_EVENT:
RTC_CHECK(false);
break;
}
}
void AecDumpBasedSimulator::HandleMessage(
const webrtc::audioproc::Config& msg) {
if (settings_.use_verbose_logging) {

View File

@ -40,6 +40,8 @@ class AecDumpBasedSimulator final : public AudioProcessingSimulator {
void Process() override;
private:
void HandleEvent(const webrtc::audioproc::Event& event_msg,
int* num_forward_chunks_processed);
void HandleMessage(const webrtc::audioproc::Init& msg);
void HandleMessage(const webrtc::audioproc::Stream& msg);
void HandleMessage(const webrtc::audioproc::ReverseStream& msg);

View File

@ -222,9 +222,12 @@ void AudioProcessingSimulator::ProcessStream(bool fixed_interface) {
if (settings_.simulate_mic_gain) {
fake_recording_device_.SetMicLevel(analog_mic_level_);
}
if (buffer_writer_) {
buffer_writer_->Write(*out_buf_);
if (buffer_memory_writer_) {
RTC_CHECK(!buffer_file_writer_);
buffer_memory_writer_->Write(*out_buf_);
} else if (buffer_file_writer_) {
RTC_CHECK(!buffer_memory_writer_);
buffer_file_writer_->Write(*out_buf_);
}
if (residual_echo_likelihood_graph_writer_.is_open()) {
@ -254,8 +257,8 @@ void AudioProcessingSimulator::ProcessReverseStream(bool fixed_interface) {
reverse_out_config_, reverse_out_buf_->channels()));
}
if (reverse_buffer_writer_) {
reverse_buffer_writer_->Write(*reverse_out_buf_);
if (reverse_buffer_file_writer_) {
reverse_buffer_file_writer_->Write(*reverse_out_buf_);
}
++num_reverse_process_stream_calls_;
@ -336,7 +339,10 @@ void AudioProcessingSimulator::SetupOutput() {
std::unique_ptr<WavWriter> out_file(
new WavWriter(filename, out_config_.sample_rate_hz(),
static_cast<size_t>(out_config_.num_channels())));
buffer_writer_.reset(new ChannelBufferWavWriter(std::move(out_file)));
buffer_file_writer_.reset(new ChannelBufferWavWriter(std::move(out_file)));
} else if (settings_.aec_dump_input_string.has_value()) {
buffer_memory_writer_ = absl::make_unique<ChannelBufferVectorWriter>(
settings_.processed_capture_samples);
}
if (settings_.reverse_output_filename) {
@ -351,7 +357,7 @@ void AudioProcessingSimulator::SetupOutput() {
std::unique_ptr<WavWriter> reverse_out_file(
new WavWriter(filename, reverse_out_config_.sample_rate_hz(),
static_cast<size_t>(reverse_out_config_.num_channels())));
reverse_buffer_writer_.reset(
reverse_buffer_file_writer_.reset(
new ChannelBufferWavWriter(std::move(reverse_out_file)));
}

View File

@ -101,6 +101,8 @@ struct SimulationSettings {
absl::optional<std::string> call_order_input_filename;
absl::optional<std::string> call_order_output_filename;
absl::optional<std::string> aec_settings_filename;
absl::optional<absl::string_view> aec_dump_input_string;
std::vector<float>* processed_capture_samples = nullptr;
};
// Copies samples present in a ChannelBuffer into an AudioFrame.
@ -172,8 +174,9 @@ class AudioProcessingSimulator {
size_t num_process_stream_calls_ = 0;
size_t num_reverse_process_stream_calls_ = 0;
std::unique_ptr<ChannelBufferWavWriter> buffer_writer_;
std::unique_ptr<ChannelBufferWavWriter> reverse_buffer_writer_;
std::unique_ptr<ChannelBufferWavWriter> buffer_file_writer_;
std::unique_ptr<ChannelBufferWavWriter> reverse_buffer_file_writer_;
std::unique_ptr<ChannelBufferVectorWriter> buffer_memory_writer_;
ApiCallStatistics api_call_statistics_;
std::ofstream residual_echo_likelihood_graph_writer_;
int analog_mic_level_;

View File

@ -462,9 +462,15 @@ void ReportConditionalErrorAndExit(bool condition, const std::string& message) {
void PerformBasicParameterSanityChecks(const SimulationSettings& settings) {
if (settings.input_filename || settings.reverse_input_filename) {
ReportConditionalErrorAndExit(!!settings.aec_dump_input_filename,
"Error: The aec dump cannot be specified "
"together with input wav files!\n");
ReportConditionalErrorAndExit(
!!settings.aec_dump_input_filename,
"Error: The aec dump file cannot be specified "
"together with input wav files!\n");
ReportConditionalErrorAndExit(
!!settings.aec_dump_input_string,
"Error: The aec dump input string cannot be specified "
"together with input wav files!\n");
ReportConditionalErrorAndExit(!!settings.artificial_nearend_filename,
"Error: The artificial nearend cannot be "
@ -480,9 +486,14 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) {
"Error: When operating at wav files, the reverse input wav filename "
"must be specified if the reverse output wav filename is specified!\n");
} else {
ReportConditionalErrorAndExit(!settings.aec_dump_input_filename,
"Error: Either the aec dump or the wav "
"input files must be specified!\n");
ReportConditionalErrorAndExit(
!settings.aec_dump_input_filename && !settings.aec_dump_input_string,
"Error: Either the aec dump input file, the wav "
"input file or the aec dump input string must be specified!\n");
ReportConditionalErrorAndExit(
settings.aec_dump_input_filename && settings.aec_dump_input_string,
"Error: The aec dump input file cannot be specified together with the "
"aec dump input string!\n");
}
ReportConditionalErrorAndExit(
@ -624,7 +635,9 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) {
int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[]) {
char* argv[],
absl::string_view input_aecdump,
std::vector<float>* processed_capture_samples) {
std::vector<char*> args = absl::ParseCommandLine(argc, argv);
if (args.size() != 1) {
printf("%s", kUsageDescription);
@ -632,10 +645,15 @@ int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
}
SimulationSettings settings = CreateSettings();
if (!input_aecdump.empty()) {
settings.aec_dump_input_string = input_aecdump;
settings.processed_capture_samples = processed_capture_samples;
RTC_CHECK(settings.processed_capture_samples);
}
PerformBasicParameterSanityChecks(settings);
std::unique_ptr<AudioProcessingSimulator> processor;
if (settings.aec_dump_input_filename) {
if (settings.aec_dump_input_filename || settings.aec_dump_input_string) {
processor.reset(new AecDumpBasedSimulator(settings, std::move(ap_builder)));
} else {
processor.reset(new WavBasedSimulator(settings, std::move(ap_builder)));

View File

@ -18,10 +18,17 @@
namespace webrtc {
namespace test {
// This function implements the audio processing simulation utility.
// This function implements the audio processing simulation utility. Pass
// |input_aecdump| to provide the content of an AEC dump file as a string; if
// |input_aecdump| is not passed, a WAV or AEC input dump file must be specified
// via the |argv| argument. Pass |processed_capture_samples| to write in it the
// samples processed on the capture side; if |processed_capture_samples| is not
// passed, the output file can optionally be specified via the |argv| argument.
int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[]);
char* argv[],
absl::string_view input_aecdump,
std::vector<float>* processed_capture_samples);
} // namespace test
} // namespace webrtc

View File

@ -10,8 +10,31 @@
#include "modules/audio_processing/test/protobuf_utils.h"
#include "absl/memory/memory.h"
#include "rtc_base/system/arch.h"
namespace {
// Allocates new memory in the memory owned by the unique_ptr to fit the raw
// message and returns the number of bytes read when having a string stream as
// input.
size_t ReadMessageBytesFromString(std::stringstream* input,
std::unique_ptr<uint8_t[]>* bytes) {
int32_t size = 0;
input->read(reinterpret_cast<char*>(&size), sizeof(int32_t));
int32_t size_read = input->gcount();
if (size_read != sizeof(int32_t))
return 0;
if (size <= 0)
return 0;
*bytes = absl::make_unique<uint8_t[]>(size);
input->read(reinterpret_cast<char*>(bytes->get()),
size * sizeof((*bytes)[0]));
size_read = input->gcount();
return size_read == size ? size : 0;
}
} // namespace
namespace webrtc {
size_t ReadMessageBytesFromFile(FILE* file, std::unique_ptr<uint8_t[]>* bytes) {
@ -26,7 +49,7 @@ size_t ReadMessageBytesFromFile(FILE* file, std::unique_ptr<uint8_t[]>* bytes) {
if (size <= 0)
return 0;
bytes->reset(new uint8_t[size]);
*bytes = absl::make_unique<uint8_t[]>(size);
return fread(bytes->get(), sizeof((*bytes)[0]), size, file);
}
@ -41,4 +64,15 @@ bool ReadMessageFromFile(FILE* file, MessageLite* msg) {
return msg->ParseFromArray(bytes.get(), size);
}
// Returns true on success, false on error or end of string stream.
bool ReadMessageFromString(std::stringstream* input, MessageLite* msg) {
std::unique_ptr<uint8_t[]> bytes;
size_t size = ReadMessageBytesFromString(input, &bytes);
if (!size)
return false;
msg->Clear();
return msg->ParseFromArray(bytes.get(), size);
}
} // namespace webrtc

View File

@ -12,6 +12,7 @@
#define MODULES_AUDIO_PROCESSING_TEST_PROTOBUF_UTILS_H_
#include <memory>
#include <sstream> // no-presubmit-check TODO(webrtc:8982)
#include "rtc_base/ignore_wundef.h"
#include "rtc_base/protobuf_utils.h"
@ -29,6 +30,11 @@ size_t ReadMessageBytesFromFile(FILE* file, std::unique_ptr<uint8_t[]>* bytes);
// Returns true on success, false on error or end-of-file.
bool ReadMessageFromFile(FILE* file, MessageLite* msg);
// Returns true on success, false on error or end of string stream.
bool ReadMessageFromString(
std::stringstream* input, // no-presubmit-check TODO(webrtc:8982)
MessageLite* msg);
} // namespace webrtc
#endif // MODULES_AUDIO_PROCESSING_TEST_PROTOBUF_UTILS_H_

View File

@ -68,6 +68,24 @@ void ChannelBufferWavWriter::Write(const ChannelBuffer<float>& buffer) {
file_->WriteSamples(&interleaved_[0], interleaved_.size());
}
ChannelBufferVectorWriter::ChannelBufferVectorWriter(std::vector<float>* output)
: output_(output) {
RTC_DCHECK(output_);
}
ChannelBufferVectorWriter::~ChannelBufferVectorWriter() = default;
void ChannelBufferVectorWriter::Write(const ChannelBuffer<float>& buffer) {
// Account for sample rate changes throughout a simulation.
interleaved_buffer_.resize(buffer.size());
Interleave(buffer.channels(), buffer.num_frames(), buffer.num_channels(),
interleaved_buffer_.data());
size_t old_size = output_->size();
output_->resize(old_size + interleaved_buffer_.size());
FloatToFloatS16(interleaved_buffer_.data(), interleaved_buffer_.size(),
output_->data() + old_size);
}
void WriteIntData(const int16_t* data,
size_t length,
WavWriter* wav_file,

View File

@ -77,6 +77,26 @@ class ChannelBufferWavWriter final {
RTC_DISALLOW_COPY_AND_ASSIGN(ChannelBufferWavWriter);
};
// Takes a pointer to a vector. Allows appending the samples of channel buffers
// to the given vector, by interleaving the samples and converting them to float
// S16.
class ChannelBufferVectorWriter final {
public:
explicit ChannelBufferVectorWriter(std::vector<float>* output);
ChannelBufferVectorWriter(const ChannelBufferVectorWriter&) = delete;
ChannelBufferVectorWriter& operator=(const ChannelBufferVectorWriter&) =
delete;
~ChannelBufferVectorWriter();
// Creates an interleaved copy of |buffer|, converts the samples to float S16
// and appends the result to output_.
void Write(const ChannelBuffer<float>& buffer);
private:
std::vector<float> interleaved_buffer_;
std::vector<float>* output_;
};
void WriteIntData(const int16_t* data,
size_t length,
WavWriter* wav_file,