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:

committed by
Commit Bot

parent
81df62b456
commit
b75d14c802
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -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)));
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Reference in New Issue
Block a user