Add ability for audioproc_f to operate on any AudioProcessing object.
This CL extends the WebRTC testing API to allow audioproc_f -based testing using a pre-created AudioProcessing object. This is an important feature to allow testing any AudioProcessing objects that are injected into WebRTC. Beyond adding this, the CL also changes the simulation code to operate on a scoped_refptr<AudioProcessing> object instead of a std::unique<AudioProcessing> object Bug: webrtc:5298 Change-Id: I70179f19518fc583ad0101bd59c038478a3cc23d Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/175568 Commit-Queue: Per Åhgren <peah@webrtc.org> Reviewed-by: Karl Wiberg <kwiberg@webrtc.org> Reviewed-by: Sam Zackrisson <saza@webrtc.org> Cr-Commit-Position: refs/heads/master@{#31319}
This commit is contained in:
@ -17,6 +17,12 @@
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
int AudioprocFloat(rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
int argc,
|
||||
char* argv[]) {
|
||||
return AudioprocFloatImpl(std::move(audio_processing), argc, argv);
|
||||
}
|
||||
|
||||
int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
|
||||
int argc,
|
||||
char* argv[]) {
|
||||
|
@ -19,6 +19,22 @@
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
// This is an interface for the audio processing simulation utility. This
|
||||
// utility can be used to simulate the audioprocessing module using a recording
|
||||
// (either an AEC dump or wav files), and generate the output as a wav file.
|
||||
// Any audio_processing object specified in the input is used for the
|
||||
// simulation. The optional |audio_processing| object provides the
|
||||
// AudioProcessing instance that is used during the simulation. Note that when
|
||||
// the audio_processing object is specified all functionality that relies on
|
||||
// using the AudioProcessingBuilder is deactivated, since the AudioProcessing
|
||||
// object is already created and the builder is not used in the simulation. It
|
||||
// is needed to pass the command line flags as |argc| and |argv|, so these can
|
||||
// be interpreted properly by the utility. To see a list of all supported
|
||||
// command line flags, run the executable with the '--help' flag.
|
||||
int AudioprocFloat(rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
int argc,
|
||||
char* argv[]);
|
||||
|
||||
// This is an interface for the audio processing simulation utility. This
|
||||
// utility can be used to simulate the audioprocessing module using a recording
|
||||
// (either an AEC dump or wav files), and generate the output as a wav file.
|
||||
|
@ -66,8 +66,11 @@ bool VerifyFloatBitExactness(const webrtc::audioproc::Stream& msg,
|
||||
|
||||
AecDumpBasedSimulator::AecDumpBasedSimulator(
|
||||
const SimulationSettings& settings,
|
||||
rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
std::unique_ptr<AudioProcessingBuilder> ap_builder)
|
||||
: AudioProcessingSimulator(settings, std::move(ap_builder)) {
|
||||
: AudioProcessingSimulator(settings,
|
||||
std::move(audio_processing),
|
||||
std::move(ap_builder)) {
|
||||
MaybeOpenCallOrderFile();
|
||||
}
|
||||
|
||||
@ -206,7 +209,8 @@ void AecDumpBasedSimulator::PrepareReverseProcessStreamCall(
|
||||
}
|
||||
|
||||
void AecDumpBasedSimulator::Process() {
|
||||
CreateAudioProcessor();
|
||||
ConfigureAudioProcessor();
|
||||
|
||||
if (settings_.artificial_nearend_filename) {
|
||||
std::unique_ptr<WavReader> artificial_nearend_file(
|
||||
new WavReader(settings_.artificial_nearend_filename->c_str()));
|
||||
@ -237,7 +241,7 @@ void AecDumpBasedSimulator::Process() {
|
||||
fclose(dump_input_file_);
|
||||
}
|
||||
|
||||
DestroyAudioProcessor();
|
||||
DetachAecDump();
|
||||
}
|
||||
|
||||
void AecDumpBasedSimulator::HandleEvent(
|
||||
|
@ -33,6 +33,7 @@ namespace test {
|
||||
class AecDumpBasedSimulator final : public AudioProcessingSimulator {
|
||||
public:
|
||||
AecDumpBasedSimulator(const SimulationSettings& settings,
|
||||
rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
std::unique_ptr<AudioProcessingBuilder> ap_builder);
|
||||
~AecDumpBasedSimulator() override;
|
||||
|
||||
|
@ -113,10 +113,10 @@ SimulationSettings::~SimulationSettings() = default;
|
||||
|
||||
AudioProcessingSimulator::AudioProcessingSimulator(
|
||||
const SimulationSettings& settings,
|
||||
rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
std::unique_ptr<AudioProcessingBuilder> ap_builder)
|
||||
: settings_(settings),
|
||||
ap_builder_(ap_builder ? std::move(ap_builder)
|
||||
: std::make_unique<AudioProcessingBuilder>()),
|
||||
ap_(std::move(audio_processing)),
|
||||
analog_mic_level_(settings.initial_mic_level),
|
||||
fake_recording_device_(
|
||||
settings.initial_mic_level,
|
||||
@ -139,6 +139,51 @@ AudioProcessingSimulator::AudioProcessingSimulator(
|
||||
|
||||
if (settings_.simulate_mic_gain)
|
||||
RTC_LOG(LS_VERBOSE) << "Simulating analog mic gain";
|
||||
|
||||
// Create the audio processing object.
|
||||
RTC_CHECK(!(ap_ && ap_builder))
|
||||
<< "The AudioProcessing and the AudioProcessingBuilder cannot both be "
|
||||
"specified at the same time.";
|
||||
|
||||
if (ap_) {
|
||||
RTC_CHECK(!settings_.aec_settings_filename);
|
||||
RTC_CHECK(!settings_.print_aec_parameter_values);
|
||||
} else {
|
||||
// Use specied builder if such is provided, otherwise create a new builder.
|
||||
std::unique_ptr<AudioProcessingBuilder> builder =
|
||||
!!ap_builder ? std::move(ap_builder)
|
||||
: std::make_unique<AudioProcessingBuilder>();
|
||||
|
||||
// Create and set an EchoCanceller3Factory if needed.
|
||||
const bool use_aec = settings_.use_aec && *settings_.use_aec;
|
||||
if (use_aec) {
|
||||
EchoCanceller3Config cfg;
|
||||
if (settings_.aec_settings_filename) {
|
||||
if (settings_.use_verbose_logging) {
|
||||
std::cout << "Reading AEC Parameters from JSON input." << std::endl;
|
||||
}
|
||||
cfg = ReadAec3ConfigFromJsonFile(*settings_.aec_settings_filename);
|
||||
}
|
||||
|
||||
if (settings_.linear_aec_output_filename) {
|
||||
cfg.filter.export_linear_aec_output = true;
|
||||
}
|
||||
|
||||
if (settings_.print_aec_parameter_values) {
|
||||
if (!settings_.use_quiet_output) {
|
||||
std::cout << "AEC settings:" << std::endl;
|
||||
}
|
||||
std::cout << Aec3ConfigToJsonString(cfg) << std::endl;
|
||||
}
|
||||
|
||||
auto echo_control_factory = std::make_unique<EchoCanceller3Factory>(cfg);
|
||||
builder->SetEchoControlFactory(std::move(echo_control_factory));
|
||||
}
|
||||
|
||||
// Create an audio processing object.
|
||||
ap_ = builder->Create();
|
||||
RTC_CHECK(ap_);
|
||||
}
|
||||
}
|
||||
|
||||
AudioProcessingSimulator::~AudioProcessingSimulator() {
|
||||
@ -369,16 +414,14 @@ void AudioProcessingSimulator::SetupOutput() {
|
||||
++output_reset_counter_;
|
||||
}
|
||||
|
||||
void AudioProcessingSimulator::DestroyAudioProcessor() {
|
||||
void AudioProcessingSimulator::DetachAecDump() {
|
||||
if (settings_.aec_dump_output_filename) {
|
||||
ap_->DetachAecDump();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioProcessingSimulator::CreateAudioProcessor() {
|
||||
Config config;
|
||||
void AudioProcessingSimulator::ConfigureAudioProcessor() {
|
||||
AudioProcessing::Config apm_config;
|
||||
std::unique_ptr<EchoControlFactory> echo_control_factory;
|
||||
if (settings_.use_ts) {
|
||||
apm_config.transient_suppression.enabled = *settings_.use_ts;
|
||||
}
|
||||
@ -421,29 +464,6 @@ void AudioProcessingSimulator::CreateAudioProcessor() {
|
||||
apm_config.echo_canceller.export_linear_aec_output =
|
||||
!!settings_.linear_aec_output_filename;
|
||||
|
||||
if (use_aec) {
|
||||
EchoCanceller3Config cfg;
|
||||
if (settings_.aec_settings_filename) {
|
||||
if (settings_.use_verbose_logging) {
|
||||
std::cout << "Reading AEC Parameters from JSON input." << std::endl;
|
||||
}
|
||||
cfg = ReadAec3ConfigFromJsonFile(*settings_.aec_settings_filename);
|
||||
}
|
||||
|
||||
if (settings_.linear_aec_output_filename) {
|
||||
cfg.filter.export_linear_aec_output = true;
|
||||
}
|
||||
|
||||
echo_control_factory.reset(new EchoCanceller3Factory(cfg));
|
||||
|
||||
if (settings_.print_aec_parameter_values) {
|
||||
if (!settings_.use_quiet_output) {
|
||||
std::cout << "AEC settings:" << std::endl;
|
||||
}
|
||||
std::cout << Aec3ConfigToJsonString(cfg) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (settings_.use_hpf) {
|
||||
apm_config.high_pass_filter.enabled = *settings_.use_hpf;
|
||||
}
|
||||
@ -512,14 +532,6 @@ void AudioProcessingSimulator::CreateAudioProcessor() {
|
||||
*settings_.ns_analysis_on_linear_aec_output;
|
||||
}
|
||||
|
||||
RTC_CHECK(ap_builder_);
|
||||
if (echo_control_factory) {
|
||||
ap_builder_->SetEchoControlFactory(std::move(echo_control_factory));
|
||||
}
|
||||
ap_.reset((*ap_builder_).Create(config));
|
||||
|
||||
RTC_CHECK(ap_);
|
||||
|
||||
ap_->ApplyConfig(apm_config);
|
||||
|
||||
if (settings_.use_ts) {
|
||||
|
@ -150,8 +150,8 @@ struct SimulationSettings {
|
||||
// Provides common functionality for performing audioprocessing simulations.
|
||||
class AudioProcessingSimulator {
|
||||
public:
|
||||
|
||||
AudioProcessingSimulator(const SimulationSettings& settings,
|
||||
rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
std::unique_ptr<AudioProcessingBuilder> ap_builder);
|
||||
virtual ~AudioProcessingSimulator();
|
||||
|
||||
@ -174,8 +174,8 @@ class AudioProcessingSimulator {
|
||||
protected:
|
||||
void ProcessStream(bool fixed_interface);
|
||||
void ProcessReverseStream(bool fixed_interface);
|
||||
void CreateAudioProcessor();
|
||||
void DestroyAudioProcessor();
|
||||
void ConfigureAudioProcessor();
|
||||
void DetachAecDump();
|
||||
void SetupBuffersConfigsOutputs(int input_sample_rate_hz,
|
||||
int output_sample_rate_hz,
|
||||
int reverse_input_sample_rate_hz,
|
||||
@ -186,8 +186,7 @@ class AudioProcessingSimulator {
|
||||
int reverse_output_num_channels);
|
||||
|
||||
const SimulationSettings settings_;
|
||||
std::unique_ptr<AudioProcessing> ap_;
|
||||
std::unique_ptr<AudioProcessingBuilder> ap_builder_;
|
||||
rtc::scoped_refptr<AudioProcessing> ap_;
|
||||
|
||||
std::unique_ptr<ChannelBuffer<float>> in_buf_;
|
||||
std::unique_ptr<ChannelBuffer<float>> out_buf_;
|
||||
|
@ -457,7 +457,10 @@ void ReportConditionalErrorAndExit(bool condition, const std::string& message) {
|
||||
}
|
||||
}
|
||||
|
||||
void PerformBasicParameterSanityChecks(const SimulationSettings& settings) {
|
||||
void PerformBasicParameterSanityChecks(
|
||||
const SimulationSettings& settings,
|
||||
bool pre_constructed_ap_provided,
|
||||
bool pre_constructed_ap_builder_provided) {
|
||||
if (settings.input_filename || settings.reverse_input_filename) {
|
||||
ReportConditionalErrorAndExit(
|
||||
!!settings.aec_dump_input_filename,
|
||||
@ -624,21 +627,41 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) {
|
||||
settings.pre_amplifier_gain_factor.has_value(),
|
||||
"Error: --pre_amplifier_gain_factor needs --pre_amplifier to be "
|
||||
"specified and set.\n");
|
||||
|
||||
ReportConditionalErrorAndExit(
|
||||
pre_constructed_ap_provided && pre_constructed_ap_builder_provided,
|
||||
"Error: The AudioProcessing and the AudioProcessingBuilder cannot both "
|
||||
"be specified at the same time.\n");
|
||||
|
||||
ReportConditionalErrorAndExit(
|
||||
settings.aec_settings_filename && pre_constructed_ap_provided,
|
||||
"Error: The aec_settings_filename cannot be specified when a "
|
||||
"pre-constructed audio processing object is provided.\n");
|
||||
|
||||
ReportConditionalErrorAndExit(
|
||||
settings.aec_settings_filename && pre_constructed_ap_provided,
|
||||
"Error: The print_aec_parameter_values cannot be set when a "
|
||||
"pre-constructed audio processing object is provided.\n");
|
||||
|
||||
if (settings.linear_aec_output_filename && pre_constructed_ap_provided) {
|
||||
std::cout << "Warning: For the linear AEC output to be stored, this must "
|
||||
"be configured in the AEC that is part of the provided "
|
||||
"AudioProcessing object."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
|
||||
int argc,
|
||||
char* argv[],
|
||||
absl::string_view input_aecdump,
|
||||
std::vector<float>* processed_capture_samples) {
|
||||
int RunSimulation(rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
std::unique_ptr<AudioProcessingBuilder> ap_builder,
|
||||
int argc,
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// InitFieldTrialsFromString stores the char*, so the char array must
|
||||
// outlive the application.
|
||||
const std::string field_trials = absl::GetFlag(FLAGS_force_fieldtrials);
|
||||
@ -650,13 +673,15 @@ int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
|
||||
settings.processed_capture_samples = processed_capture_samples;
|
||||
RTC_CHECK(settings.processed_capture_samples);
|
||||
}
|
||||
PerformBasicParameterSanityChecks(settings);
|
||||
PerformBasicParameterSanityChecks(settings, !!audio_processing, !!ap_builder);
|
||||
std::unique_ptr<AudioProcessingSimulator> processor;
|
||||
|
||||
if (settings.aec_dump_input_filename || settings.aec_dump_input_string) {
|
||||
processor.reset(new AecDumpBasedSimulator(settings, std::move(ap_builder)));
|
||||
processor.reset(new AecDumpBasedSimulator(
|
||||
settings, std::move(audio_processing), std::move(ap_builder)));
|
||||
} else {
|
||||
processor.reset(new WavBasedSimulator(settings, std::move(ap_builder)));
|
||||
processor.reset(new WavBasedSimulator(settings, std::move(audio_processing),
|
||||
std::move(ap_builder)));
|
||||
}
|
||||
|
||||
processor->Process();
|
||||
@ -680,5 +705,24 @@ int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int AudioprocFloatImpl(rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
int argc,
|
||||
char* argv[]) {
|
||||
return RunSimulation(
|
||||
std::move(audio_processing), /*ap_builder=*/nullptr, argc, argv,
|
||||
/*input_aecdump=*/"", /*processed_capture_samples=*/nullptr);
|
||||
}
|
||||
|
||||
int AudioprocFloatImpl(std::unique_ptr<AudioProcessingBuilder> ap_builder,
|
||||
int argc,
|
||||
char* argv[],
|
||||
absl::string_view input_aecdump,
|
||||
std::vector<float>* processed_capture_samples) {
|
||||
return RunSimulation(/*audio_processing=*/nullptr, std::move(ap_builder),
|
||||
argc, argv, input_aecdump, processed_capture_samples);
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
} // namespace webrtc
|
||||
|
@ -18,6 +18,21 @@
|
||||
namespace webrtc {
|
||||
namespace test {
|
||||
|
||||
// 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.
|
||||
// Any audio_processing object specified in the input is used for the
|
||||
// simulation. Note that when the audio_processing object is specified all
|
||||
// functionality that relies on using the internal builder is deactivated,
|
||||
// since the AudioProcessing object is already created and the builder is not
|
||||
// used in the simulation.
|
||||
int AudioprocFloatImpl(rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
int argc,
|
||||
char* argv[]);
|
||||
|
||||
// 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
|
||||
|
@ -56,8 +56,18 @@ WavBasedSimulator::GetCustomEventChain(const std::string& filename) {
|
||||
|
||||
WavBasedSimulator::WavBasedSimulator(
|
||||
const SimulationSettings& settings,
|
||||
rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
std::unique_ptr<AudioProcessingBuilder> ap_builder)
|
||||
: AudioProcessingSimulator(settings, std::move(ap_builder)) {}
|
||||
: AudioProcessingSimulator(settings,
|
||||
std::move(audio_processing),
|
||||
std::move(ap_builder)) {
|
||||
if (settings_.call_order_input_filename) {
|
||||
call_chain_ = WavBasedSimulator::GetCustomEventChain(
|
||||
*settings_.call_order_input_filename);
|
||||
} else {
|
||||
call_chain_ = WavBasedSimulator::GetDefaultEventChain();
|
||||
}
|
||||
}
|
||||
|
||||
WavBasedSimulator::~WavBasedSimulator() = default;
|
||||
|
||||
@ -89,13 +99,7 @@ void WavBasedSimulator::PrepareReverseProcessStreamCall() {
|
||||
}
|
||||
|
||||
void WavBasedSimulator::Process() {
|
||||
if (settings_.call_order_input_filename) {
|
||||
call_chain_ = WavBasedSimulator::GetCustomEventChain(
|
||||
*settings_.call_order_input_filename);
|
||||
} else {
|
||||
call_chain_ = WavBasedSimulator::GetDefaultEventChain();
|
||||
}
|
||||
CreateAudioProcessor();
|
||||
ConfigureAudioProcessor();
|
||||
|
||||
Initialize();
|
||||
|
||||
@ -120,7 +124,7 @@ void WavBasedSimulator::Process() {
|
||||
call_chain_index = (call_chain_index + 1) % call_chain_.size();
|
||||
}
|
||||
|
||||
DestroyAudioProcessor();
|
||||
DetachAecDump();
|
||||
}
|
||||
|
||||
bool WavBasedSimulator::HandleProcessStreamCall() {
|
||||
|
@ -23,6 +23,7 @@ namespace test {
|
||||
class WavBasedSimulator final : public AudioProcessingSimulator {
|
||||
public:
|
||||
WavBasedSimulator(const SimulationSettings& settings,
|
||||
rtc::scoped_refptr<AudioProcessing> audio_processing,
|
||||
std::unique_ptr<AudioProcessingBuilder> ap_builder);
|
||||
~WavBasedSimulator() override;
|
||||
|
||||
|
Reference in New Issue
Block a user