This CL updates and extends the audioproc_f command line

tool to support all the functionality needed for simulating
and analyzing the audio processing module behavior during
calls.

BUG=

Review-Url: https://codereview.webrtc.org/1907223003
Cr-Commit-Position: refs/heads/master@{#12882}
This commit is contained in:
peah 2016-05-24 20:54:40 -07:00 committed by Commit bot
parent 13deaad1bd
commit 60a189f1bf
12 changed files with 1783 additions and 517 deletions

View File

@ -129,8 +129,12 @@
'<(DEPTH)/third_party/gflags/gflags.gyp:gflags',
],
'sources': [
'test/audio_file_processor.cc',
'test/audio_file_processor.h',
'test/audio_processing_simulator.cc',
'test/audio_processing_simulator.h',
'test/aec_dump_based_simulator.cc',
'test/aec_dump_based_simulator.h',
'test/wav_based_simulator.cc',
'test/wav_based_simulator.h',
'test/audioproc_float.cc',
],
},

View File

@ -0,0 +1,506 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <iostream>
#include "webrtc/modules/audio_processing/test/aec_dump_based_simulator.h"
#include "webrtc/base/checks.h"
#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/test/testsupport/trace_to_stderr.h"
namespace webrtc {
namespace test {
namespace {
// Verify output bitexactness for the fixed interface.
// TODO(peah): Check whether it would make sense to add a threshold
// to use for checking the bitexactness in a soft manner.
bool VerifyFixedBitExactness(const webrtc::audioproc::Stream& msg,
const AudioFrame& frame) {
if ((sizeof(int16_t) * frame.samples_per_channel_ * frame.num_channels_) !=
msg.output_data().size()) {
return false;
} else {
for (size_t k = 0; k < frame.num_channels_ * frame.samples_per_channel_;
++k) {
if (msg.output_data().data()[k] != frame.data_[k]) {
return false;
}
}
}
return true;
}
// Verify output bitexactness for the float interface.
bool VerifyFloatBitExactness(const webrtc::audioproc::Stream& msg,
const StreamConfig& out_config,
const ChannelBuffer<float>& out_buf) {
if (static_cast<size_t>(msg.output_channel_size()) !=
out_config.num_channels() ||
msg.output_channel(0).size() != out_config.num_frames()) {
return false;
} else {
for (int ch = 0; ch < msg.output_channel_size(); ++ch) {
for (size_t sample = 0; sample < out_config.num_frames(); ++sample) {
if (msg.output_channel(ch).data()[sample] !=
out_buf.channels()[ch][sample]) {
return false;
}
}
}
}
return true;
}
} // namespace
void AecDumpBasedSimulator::PrepareProcessStreamCall(
const webrtc::audioproc::Stream& msg) {
if (msg.has_input_data()) {
// Fixed interface processing.
// Verify interface invariance.
RTC_CHECK(interface_used_ == InterfaceType::kFixedInterface ||
interface_used_ == InterfaceType::kNotSpecified);
interface_used_ = InterfaceType::kFixedInterface;
// Populate input buffer.
RTC_CHECK_EQ(sizeof(fwd_frame_.data_[0]) * fwd_frame_.samples_per_channel_ *
fwd_frame_.num_channels_,
msg.input_data().size());
memcpy(fwd_frame_.data_, msg.input_data().data(), msg.input_data().size());
} else {
// Float interface processing.
// Verify interface invariance.
RTC_CHECK(interface_used_ == InterfaceType::kFloatInterface ||
interface_used_ == InterfaceType::kNotSpecified);
interface_used_ = InterfaceType::kFloatInterface;
RTC_CHECK_EQ(in_buf_->num_channels(),
static_cast<size_t>(msg.input_channel_size()));
// Populate input buffer.
for (int i = 0; i < msg.input_channel_size(); ++i) {
RTC_CHECK_EQ(in_buf_->num_frames() * sizeof(*in_buf_->channels()[i]),
msg.input_channel(i).size());
std::memcpy(in_buf_->channels()[i], msg.input_channel(i).data(),
msg.input_channel(i).size());
}
}
if (!settings_.stream_delay) {
if (msg.has_delay()) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->set_stream_delay_ms(msg.delay()));
}
} else {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->set_stream_delay_ms(*settings_.stream_delay));
}
if (!settings_.stream_drift_samples) {
if (msg.has_drift()) {
ap_->echo_cancellation()->set_stream_drift_samples(msg.drift());
}
} else {
ap_->echo_cancellation()->set_stream_drift_samples(
*settings_.stream_drift_samples);
}
if (!settings_.use_ts) {
if (msg.has_keypress()) {
ap_->set_stream_key_pressed(msg.keypress());
}
} else {
ap_->set_stream_key_pressed(*settings_.use_ts);
}
// TODO(peah): Add support for controlling the analog level via the
// command-line.
if (msg.has_level()) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->gain_control()->set_stream_analog_level(msg.level()));
}
}
void AecDumpBasedSimulator::VerifyProcessStreamBitExactness(
const webrtc::audioproc::Stream& msg) {
if (bitexact_output_) {
if (interface_used_ == InterfaceType::kFixedInterface) {
bitexact_output_ = VerifyFixedBitExactness(msg, fwd_frame_);
} else {
bitexact_output_ = VerifyFloatBitExactness(msg, out_config_, *out_buf_);
}
}
}
void AecDumpBasedSimulator::PrepareReverseProcessStreamCall(
const webrtc::audioproc::ReverseStream& msg) {
if (msg.has_data()) {
// Fixed interface processing.
// Verify interface invariance.
RTC_CHECK(interface_used_ == InterfaceType::kFixedInterface ||
interface_used_ == InterfaceType::kNotSpecified);
interface_used_ = InterfaceType::kFixedInterface;
// Populate input buffer.
RTC_CHECK_EQ(sizeof(int16_t) * rev_frame_.samples_per_channel_ *
rev_frame_.num_channels_,
msg.data().size());
memcpy(rev_frame_.data_, msg.data().data(), msg.data().size());
} else {
// Float interface processing.
// Verify interface invariance.
RTC_CHECK(interface_used_ == InterfaceType::kFloatInterface ||
interface_used_ == InterfaceType::kNotSpecified);
interface_used_ = InterfaceType::kFloatInterface;
RTC_CHECK_EQ(reverse_in_buf_->num_channels(),
static_cast<size_t>(msg.channel_size()));
// Populate input buffer.
for (int i = 0; i < msg.channel_size(); ++i) {
RTC_CHECK_EQ(reverse_in_buf_->num_frames() *
sizeof(*reverse_in_buf_->channels()[i]),
msg.channel(i).size());
std::memcpy(reverse_in_buf_->channels()[i], msg.channel(i).data(),
msg.channel(i).size());
}
}
}
void AecDumpBasedSimulator::Process() {
std::unique_ptr<test::TraceToStderr> trace_to_stderr;
if (settings_.use_verbose_logging) {
trace_to_stderr.reset(new test::TraceToStderr(true));
}
CreateAudioProcessor();
dump_input_file_ = OpenFile(settings_.aec_dump_input_filename->c_str(), "rb");
webrtc::audioproc::Event event_msg;
int num_forward_chunks_processed = 0;
const float kOneBykChunksPerSecond =
1.f / AudioProcessingSimulator::kChunksPerSecond;
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;
default:
RTC_CHECK(false);
}
if (trace_to_stderr) {
trace_to_stderr->SetTimeSeconds(num_forward_chunks_processed *
kOneBykChunksPerSecond);
}
}
fclose(dump_input_file_);
DestroyAudioProcessor();
}
void AecDumpBasedSimulator::HandleMessage(
const webrtc::audioproc::Config& msg) {
if (settings_.use_verbose_logging) {
std::cout << "Config at frame:" << std::endl;
std::cout << " Forward: " << get_num_process_stream_calls() << std::endl;
std::cout << " Reverse: " << get_num_reverse_process_stream_calls()
<< std::endl;
}
if (!settings_.discard_all_settings_in_aecdump) {
if (settings_.use_verbose_logging) {
std::cout << "Setting used in config:" << std::endl;
}
Config config;
if (msg.has_aec_enabled() || settings_.use_aec) {
bool enable = settings_.use_aec ? *settings_.use_aec : msg.aec_enabled();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_cancellation()->Enable(enable));
if (settings_.use_verbose_logging) {
std::cout << " aec_enabled: " << (enable ? "true" : "false")
<< std::endl;
}
}
if (msg.has_aec_delay_agnostic_enabled() || settings_.use_delay_agnostic) {
bool enable = settings_.use_delay_agnostic
? *settings_.use_delay_agnostic
: msg.aec_delay_agnostic_enabled();
config.Set<DelayAgnostic>(new DelayAgnostic(enable));
if (settings_.use_verbose_logging) {
std::cout << " aec_delay_agnostic_enabled: "
<< (enable ? "true" : "false") << std::endl;
}
}
if (msg.has_aec_drift_compensation_enabled() ||
settings_.use_drift_compensation) {
bool enable = settings_.use_drift_compensation
? *settings_.use_drift_compensation
: msg.aec_drift_compensation_enabled();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_cancellation()->enable_drift_compensation(enable));
if (settings_.use_verbose_logging) {
std::cout << " aec_drift_compensation_enabled: "
<< (enable ? "true" : "false") << std::endl;
}
}
if (msg.has_aec_extended_filter_enabled() ||
settings_.use_extended_filter) {
bool enable = settings_.use_extended_filter
? *settings_.use_extended_filter
: msg.aec_extended_filter_enabled();
config.Set<ExtendedFilter>(new ExtendedFilter(enable));
if (settings_.use_verbose_logging) {
std::cout << " aec_extended_filter_enabled: "
<< (enable ? "true" : "false") << std::endl;
}
}
if (msg.has_aec_suppression_level() || settings_.aec_suppression_level) {
int level = settings_.aec_suppression_level
? *settings_.aec_suppression_level
: msg.aec_suppression_level();
RTC_CHECK_EQ(
AudioProcessing::kNoError,
ap_->echo_cancellation()->set_suppression_level(
static_cast<webrtc::EchoCancellation::SuppressionLevel>(level)));
if (settings_.use_verbose_logging) {
std::cout << " aec_suppression_level: " << level << std::endl;
}
}
if (msg.has_aecm_enabled() || settings_.use_aecm) {
bool enable =
settings_.use_aecm ? *settings_.use_aecm : msg.aecm_enabled();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_control_mobile()->Enable(enable));
if (settings_.use_verbose_logging) {
std::cout << " aecm_enabled: " << (enable ? "true" : "false")
<< std::endl;
}
}
if (msg.has_aecm_comfort_noise_enabled() ||
settings_.use_aecm_comfort_noise) {
bool enable = settings_.use_aecm_comfort_noise
? *settings_.use_aecm_comfort_noise
: msg.aecm_comfort_noise_enabled();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_control_mobile()->enable_comfort_noise(enable));
if (settings_.use_verbose_logging) {
std::cout << " aecm_comfort_noise_enabled: "
<< (enable ? "true" : "false") << std::endl;
}
}
if (msg.has_aecm_routing_mode() || settings_.aecm_routing_mode) {
int routing_mode = settings_.aecm_routing_mode
? *settings_.aecm_routing_mode
: msg.aecm_routing_mode();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_control_mobile()->set_routing_mode(
static_cast<webrtc::EchoControlMobile::RoutingMode>(
routing_mode)));
if (settings_.use_verbose_logging) {
std::cout << " aecm_routing_mode: " << routing_mode << std::endl;
}
}
if (msg.has_agc_enabled() || settings_.use_agc) {
bool enable = settings_.use_agc ? *settings_.use_agc : msg.agc_enabled();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->gain_control()->Enable(enable));
if (settings_.use_verbose_logging) {
std::cout << " agc_enabled: " << (enable ? "true" : "false")
<< std::endl;
}
}
if (msg.has_agc_mode() || settings_.agc_mode) {
int mode = settings_.agc_mode ? *settings_.agc_mode : msg.agc_mode();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->gain_control()->set_mode(
static_cast<webrtc::GainControl::Mode>(mode)));
if (settings_.use_verbose_logging) {
std::cout << " agc_mode: " << mode << std::endl;
}
}
if (msg.has_agc_limiter_enabled() || settings_.use_agc_limiter) {
bool enable = settings_.use_agc_limiter ? *settings_.use_agc_limiter
: msg.agc_limiter_enabled();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->gain_control()->enable_limiter(enable));
if (settings_.use_verbose_logging) {
std::cout << " agc_limiter_enabled: " << (enable ? "true" : "false")
<< std::endl;
}
}
// TODO(peah): Add support for controlling the Experimental AGC from the
// command line.
if (msg.has_noise_robust_agc_enabled()) {
config.Set<ExperimentalAgc>(
new ExperimentalAgc(msg.noise_robust_agc_enabled()));
if (settings_.use_verbose_logging) {
std::cout << " noise_robust_agc_enabled: "
<< (msg.noise_robust_agc_enabled() ? "true" : "false")
<< std::endl;
}
}
if (msg.has_transient_suppression_enabled() || settings_.use_ts) {
bool enable = settings_.use_ts ? *settings_.use_ts
: msg.transient_suppression_enabled();
config.Set<ExperimentalNs>(new ExperimentalNs(enable));
if (settings_.use_verbose_logging) {
std::cout << " transient_suppression_enabled: "
<< (enable ? "true" : "false") << std::endl;
}
}
if (msg.has_hpf_enabled() || settings_.use_hpf) {
bool enable = settings_.use_hpf ? *settings_.use_hpf : msg.hpf_enabled();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->high_pass_filter()->Enable(enable));
if (settings_.use_verbose_logging) {
std::cout << " hpf_enabled: " << (enable ? "true" : "false")
<< std::endl;
}
}
if (msg.has_ns_enabled() || settings_.use_ns) {
bool enable = settings_.use_ns ? *settings_.use_ns : msg.ns_enabled();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->noise_suppression()->Enable(enable));
if (settings_.use_verbose_logging) {
std::cout << " ns_enabled: " << (enable ? "true" : "false")
<< std::endl;
}
}
if (msg.has_ns_level() || settings_.ns_level) {
int level = settings_.ns_level ? *settings_.ns_level : msg.ns_level();
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->noise_suppression()->set_level(
static_cast<NoiseSuppression::Level>(level)));
if (settings_.use_verbose_logging) {
std::cout << " ns_level: " << level << std::endl;
}
}
if (settings_.use_verbose_logging && msg.has_experiments_description() &&
msg.experiments_description().size() > 0) {
std::cout << " experiments not included by default in the simulation: "
<< msg.experiments_description() << std::endl;
}
if (settings_.use_refined_adaptive_filter) {
config.Set<RefinedAdaptiveFilter>(
new RefinedAdaptiveFilter(*settings_.use_refined_adaptive_filter));
}
if (settings_.use_aec3) {
config.Set<EchoCanceller3>(new EchoCanceller3(*settings_.use_aec3));
}
ap_->SetExtraOptions(config);
}
}
void AecDumpBasedSimulator::HandleMessage(const webrtc::audioproc::Init& msg) {
RTC_CHECK(msg.has_sample_rate());
RTC_CHECK(msg.has_num_input_channels());
RTC_CHECK(msg.has_num_reverse_channels());
RTC_CHECK(msg.has_reverse_sample_rate());
if (settings_.use_verbose_logging) {
std::cout << "Init at frame:" << std::endl;
std::cout << " Forward: " << get_num_process_stream_calls() << std::endl;
std::cout << " Reverse: " << get_num_reverse_process_stream_calls()
<< std::endl;
}
int num_output_channels;
if (settings_.output_num_channels) {
num_output_channels = *settings_.output_num_channels;
} else {
num_output_channels = msg.has_num_output_channels()
? msg.num_output_channels()
: msg.num_input_channels();
}
int output_sample_rate;
if (settings_.output_sample_rate_hz) {
output_sample_rate = *settings_.output_sample_rate_hz;
} else {
output_sample_rate = msg.has_output_sample_rate() ? msg.output_sample_rate()
: msg.sample_rate();
}
int num_reverse_output_channels;
if (settings_.reverse_output_num_channels) {
num_reverse_output_channels = *settings_.reverse_output_num_channels;
} else {
num_reverse_output_channels = msg.has_num_reverse_output_channels()
? msg.num_reverse_output_channels()
: msg.num_reverse_channels();
}
int reverse_output_sample_rate;
if (settings_.reverse_output_sample_rate_hz) {
reverse_output_sample_rate = *settings_.reverse_output_sample_rate_hz;
} else {
reverse_output_sample_rate = msg.has_reverse_output_sample_rate()
? msg.reverse_output_sample_rate()
: msg.reverse_sample_rate();
}
SetupBuffersConfigsOutputs(
msg.sample_rate(), output_sample_rate, msg.reverse_sample_rate(),
reverse_output_sample_rate, msg.num_input_channels(), num_output_channels,
msg.num_reverse_channels(), num_reverse_output_channels);
}
void AecDumpBasedSimulator::HandleMessage(
const webrtc::audioproc::Stream& msg) {
PrepareProcessStreamCall(msg);
ProcessStream(interface_used_ == InterfaceType::kFixedInterface);
VerifyProcessStreamBitExactness(msg);
}
void AecDumpBasedSimulator::HandleMessage(
const webrtc::audioproc::ReverseStream& msg) {
PrepareReverseProcessStreamCall(msg);
ProcessReverseStream(interface_used_ == InterfaceType::kFixedInterface);
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AEC_DUMP_BASED_SIMULATOR_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AEC_DUMP_BASED_SIMULATOR_H_
#include "webrtc/modules/audio_processing/test/audio_processing_simulator.h"
#include "webrtc/base/constructormagic.h"
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
#else
#include "webrtc/modules/audio_processing/debug.pb.h"
#endif
namespace webrtc {
namespace test {
// Used to perform an audio processing simulation from an aec dump.
class AecDumpBasedSimulator final : public AudioProcessingSimulator {
public:
explicit AecDumpBasedSimulator(const SimulationSettings& settings)
: AudioProcessingSimulator(settings) {}
virtual ~AecDumpBasedSimulator() {}
// Processes the messages in the aecdump file.
void Process() override;
private:
void HandleMessage(const webrtc::audioproc::Init& msg);
void HandleMessage(const webrtc::audioproc::Stream& msg);
void HandleMessage(const webrtc::audioproc::ReverseStream& msg);
void HandleMessage(const webrtc::audioproc::Config& msg);
void PrepareProcessStreamCall(const webrtc::audioproc::Stream& msg);
void PrepareReverseProcessStreamCall(
const webrtc::audioproc::ReverseStream& msg);
void VerifyProcessStreamBitExactness(const webrtc::audioproc::Stream& msg);
enum InterfaceType {
kFixedInterface,
kFloatInterface,
kNotSpecified,
};
FILE* dump_input_file_;
InterfaceType interface_used_ = InterfaceType::kNotSpecified;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(AecDumpBasedSimulator);
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AEC_DUMP_BASED_SIMULATOR_H_

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AEC_DUMP_PROCESSOR_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AEC_DUMP_PROCESSOR_H_
#include <algorithm>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include "webrtc/base/timeutils.h"
#include "webrtc/base/optional.h"
#include "webrtc/common_audio/channel_buffer.h"
#include "webrtc/common_audio/wav_file.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/test/audio_file_processor.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
#else
#include "webrtc/modules/audio_processing/debug.pb.h"
#endif
namespace webrtc {
namespace test {
// Used to read from an aecdump file and write to a WavWriter.
class AecDumpFileProcessor final : public AudioFileProcessor {
public:
AecDumpFileProcessor(std::unique_ptr<AudioProcessing> ap,
FILE* dump_file,
std::string out_filename,
std::string reverse_out_filename,
rtc::Optional<int> out_sample_rate_hz,
rtc::Optional<int> out_num_channels,
rtc::Optional<int> reverse_out_sample_rate_hz,
rtc::Optional<int> reverse_out_num_channels,
bool override_config_message);
virtual ~AecDumpFileProcessor();
// Processes the messages in the aecdump file and returns
// the number of forward stream chunks processed.
size_t Process(bool verbose_logging) override;
private:
void HandleMessage(const webrtc::audioproc::Init& msg);
void HandleMessage(const webrtc::audioproc::Stream& msg);
void HandleMessage(const webrtc::audioproc::ReverseStream& msg);
void HandleMessage(const webrtc::audioproc::Config& msg);
enum InterfaceType {
kIntInterface,
kFloatInterface,
kNotSpecified,
};
std::unique_ptr<AudioProcessing> ap_;
FILE* dump_file_;
std::string out_filename_;
std::string reverse_out_filename_;
rtc::Optional<int> out_sample_rate_hz_;
rtc::Optional<int> out_num_channels_;
rtc::Optional<int> reverse_out_sample_rate_hz_;
rtc::Optional<int> reverse_out_num_channels_;
bool override_config_message_;
std::unique_ptr<ChannelBuffer<float>> in_buf_;
std::unique_ptr<ChannelBuffer<float>> reverse_buf_;
std::unique_ptr<ChannelBuffer<float>> out_buf_;
std::unique_ptr<ChannelBuffer<float>> reverse_out_buf_;
std::unique_ptr<WavWriter> out_file_;
std::unique_ptr<WavWriter> reverse_out_file_;
StreamConfig input_config_;
StreamConfig reverse_config_;
StreamConfig output_config_;
StreamConfig reverse_output_config_;
std::unique_ptr<ChannelBufferWavWriter> buffer_writer_;
std::unique_ptr<ChannelBufferWavWriter> reverse_buffer_writer_;
AudioFrame far_frame_;
AudioFrame near_frame_;
InterfaceType interface_used_ = InterfaceType::kNotSpecified;
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AEC_DUMP_PROCESSOR_H_

View File

@ -1,220 +0,0 @@
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_processing/test/audio_file_processor.h"
#include <algorithm>
#include <utility>
#include "webrtc/base/checks.h"
#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
using rtc::CheckedDivExact;
using std::vector;
using webrtc::audioproc::Event;
using webrtc::audioproc::Init;
using webrtc::audioproc::ReverseStream;
using webrtc::audioproc::Stream;
namespace webrtc {
namespace {
// Returns a StreamConfig corresponding to file.
StreamConfig GetStreamConfig(const WavFile& file) {
return StreamConfig(file.sample_rate(), file.num_channels());
}
// Returns a ChannelBuffer corresponding to file.
ChannelBuffer<float> GetChannelBuffer(const WavFile& file) {
return ChannelBuffer<float>(
CheckedDivExact(file.sample_rate(), AudioFileProcessor::kChunksPerSecond),
file.num_channels());
}
} // namespace
WavFileProcessor::WavFileProcessor(std::unique_ptr<AudioProcessing> ap,
std::unique_ptr<WavReader> in_file,
std::unique_ptr<WavWriter> out_file,
std::unique_ptr<WavReader> reverse_in_file,
std::unique_ptr<WavWriter> reverse_out_file)
: ap_(std::move(ap)),
in_buf_(GetChannelBuffer(*in_file)),
out_buf_(GetChannelBuffer(*out_file)),
input_config_(GetStreamConfig(*in_file)),
output_config_(GetStreamConfig(*out_file)),
buffer_reader_(std::move(in_file)),
buffer_writer_(std::move(out_file)) {
if (reverse_in_file) {
const WavFile* reverse_out_config;
if (reverse_out_file) {
reverse_out_config = reverse_out_file.get();
} else {
reverse_out_config = reverse_in_file.get();
}
reverse_in_buf_.reset(
new ChannelBuffer<float>(GetChannelBuffer(*reverse_in_file)));
reverse_out_buf_.reset(
new ChannelBuffer<float>(GetChannelBuffer(*reverse_out_config)));
reverse_input_config_.reset(
new StreamConfig(GetStreamConfig(*reverse_in_file)));
reverse_output_config_.reset(
new StreamConfig(GetStreamConfig(*reverse_out_config)));
reverse_buffer_reader_.reset(
new ChannelBufferWavReader(std::move(reverse_in_file)));
if (reverse_out_file) {
reverse_buffer_writer_.reset(
new ChannelBufferWavWriter(std::move(reverse_out_file)));
}
}
}
bool WavFileProcessor::ProcessChunk() {
if (!buffer_reader_.Read(&in_buf_)) {
return false;
}
{
const auto st = ScopedTimer(mutable_proc_time());
RTC_CHECK_EQ(kNoErr,
ap_->ProcessStream(in_buf_.channels(), input_config_,
output_config_, out_buf_.channels()));
}
buffer_writer_.Write(out_buf_);
if (reverse_buffer_reader_) {
if (!reverse_buffer_reader_->Read(reverse_in_buf_.get())) {
return false;
}
{
const auto st = ScopedTimer(mutable_proc_time());
RTC_CHECK_EQ(kNoErr,
ap_->ProcessReverseStream(reverse_in_buf_->channels(),
*reverse_input_config_.get(),
*reverse_output_config_.get(),
reverse_out_buf_->channels()));
}
if (reverse_buffer_writer_) {
reverse_buffer_writer_->Write(*reverse_out_buf_.get());
}
}
return true;
}
AecDumpFileProcessor::AecDumpFileProcessor(std::unique_ptr<AudioProcessing> ap,
FILE* dump_file,
std::unique_ptr<WavWriter> out_file)
: ap_(std::move(ap)),
dump_file_(dump_file),
out_buf_(GetChannelBuffer(*out_file)),
output_config_(GetStreamConfig(*out_file)),
buffer_writer_(std::move(out_file)) {
RTC_CHECK(dump_file_) << "Could not open dump file for reading.";
}
AecDumpFileProcessor::~AecDumpFileProcessor() {
fclose(dump_file_);
}
bool AecDumpFileProcessor::ProcessChunk() {
Event event_msg;
// Continue until we process our first Stream message.
do {
if (!ReadMessageFromFile(dump_file_, &event_msg)) {
return false;
}
if (event_msg.type() == Event::INIT) {
RTC_CHECK(event_msg.has_init());
HandleMessage(event_msg.init());
} else if (event_msg.type() == Event::STREAM) {
RTC_CHECK(event_msg.has_stream());
HandleMessage(event_msg.stream());
} else if (event_msg.type() == Event::REVERSE_STREAM) {
RTC_CHECK(event_msg.has_reverse_stream());
HandleMessage(event_msg.reverse_stream());
}
} while (event_msg.type() != Event::STREAM);
return true;
}
void AecDumpFileProcessor::HandleMessage(const Init& msg) {
RTC_CHECK(msg.has_sample_rate());
RTC_CHECK(msg.has_num_input_channels());
RTC_CHECK(msg.has_num_reverse_channels());
in_buf_.reset(new ChannelBuffer<float>(
CheckedDivExact(msg.sample_rate(), kChunksPerSecond),
msg.num_input_channels()));
const int reverse_sample_rate = msg.has_reverse_sample_rate()
? msg.reverse_sample_rate()
: msg.sample_rate();
reverse_buf_.reset(new ChannelBuffer<float>(
CheckedDivExact(reverse_sample_rate, kChunksPerSecond),
msg.num_reverse_channels()));
input_config_ = StreamConfig(msg.sample_rate(), msg.num_input_channels());
reverse_config_ =
StreamConfig(reverse_sample_rate, msg.num_reverse_channels());
const ProcessingConfig config = {
{input_config_, output_config_, reverse_config_, reverse_config_}};
RTC_CHECK_EQ(kNoErr, ap_->Initialize(config));
}
void AecDumpFileProcessor::HandleMessage(const Stream& msg) {
RTC_CHECK(!msg.has_input_data());
RTC_CHECK_EQ(in_buf_->num_channels(),
static_cast<size_t>(msg.input_channel_size()));
for (int i = 0; i < msg.input_channel_size(); ++i) {
RTC_CHECK_EQ(in_buf_->num_frames() * sizeof(*in_buf_->channels()[i]),
msg.input_channel(i).size());
std::memcpy(in_buf_->channels()[i], msg.input_channel(i).data(),
msg.input_channel(i).size());
}
{
const auto st = ScopedTimer(mutable_proc_time());
RTC_CHECK_EQ(kNoErr, ap_->set_stream_delay_ms(msg.delay()));
ap_->echo_cancellation()->set_stream_drift_samples(msg.drift());
if (msg.has_keypress()) {
ap_->set_stream_key_pressed(msg.keypress());
}
RTC_CHECK_EQ(kNoErr,
ap_->ProcessStream(in_buf_->channels(), input_config_,
output_config_, out_buf_.channels()));
}
buffer_writer_.Write(out_buf_);
}
void AecDumpFileProcessor::HandleMessage(const ReverseStream& msg) {
RTC_CHECK(!msg.has_data());
RTC_CHECK_EQ(reverse_buf_->num_channels(),
static_cast<size_t>(msg.channel_size()));
for (int i = 0; i < msg.channel_size(); ++i) {
RTC_CHECK_EQ(reverse_buf_->num_frames() * sizeof(*in_buf_->channels()[i]),
msg.channel(i).size());
std::memcpy(reverse_buf_->channels()[i], msg.channel(i).data(),
msg.channel(i).size());
}
{
const auto st = ScopedTimer(mutable_proc_time());
// TODO(ajm): This currently discards the processed output, which is needed
// for e.g. intelligibility enhancement.
RTC_CHECK_EQ(kNoErr, ap_->ProcessReverseStream(
reverse_buf_->channels(), reverse_config_,
reverse_config_, reverse_buf_->channels()));
}
}
} // namespace webrtc

View File

@ -1,147 +0,0 @@
/*
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AUDIO_FILE_PROCESSOR_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AUDIO_FILE_PROCESSOR_H_
#include <algorithm>
#include <limits>
#include <memory>
#include <vector>
#include "webrtc/base/timeutils.h"
#include "webrtc/common_audio/channel_buffer.h"
#include "webrtc/common_audio/wav_file.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#ifdef WEBRTC_ANDROID_PLATFORM_BUILD
#include "external/webrtc/webrtc/modules/audio_processing/debug.pb.h"
#else
#include "webrtc/modules/audio_processing/debug.pb.h"
#endif
namespace webrtc {
// Holds a few statistics about a series of TickIntervals.
struct TickIntervalStats {
TickIntervalStats() : min(std::numeric_limits<int64_t>::max()) {}
int64_t sum;
int64_t max;
int64_t min;
};
// Interface for processing an input file with an AudioProcessing instance and
// dumping the results to an output file.
class AudioFileProcessor {
public:
static const int kChunksPerSecond = 1000 / AudioProcessing::kChunkSizeMs;
virtual ~AudioFileProcessor() {}
// Processes one AudioProcessing::kChunkSizeMs of data from the input file and
// writes to the output file.
virtual bool ProcessChunk() = 0;
// Returns the execution time of all AudioProcessing calls.
const TickIntervalStats& proc_time() const { return proc_time_; }
protected:
// RAII class for execution time measurement. Updates the provided
// TickIntervalStats based on the time between ScopedTimer creation and
// leaving the enclosing scope.
class ScopedTimer {
public:
explicit ScopedTimer(TickIntervalStats* proc_time)
: proc_time_(proc_time), start_time_(rtc::TimeNanos()) {}
~ScopedTimer() {
int64_t interval = rtc::TimeNanos() - start_time_;
proc_time_->sum += interval;
proc_time_->max = std::max(proc_time_->max, interval);
proc_time_->min = std::min(proc_time_->min, interval);
}
private:
TickIntervalStats* const proc_time_;
int64_t start_time_;
};
TickIntervalStats* mutable_proc_time() { return &proc_time_; }
private:
TickIntervalStats proc_time_;
};
// Used to read from and write to WavFile objects.
class WavFileProcessor final : public AudioFileProcessor {
public:
// Takes ownership of all parameters.
WavFileProcessor(std::unique_ptr<AudioProcessing> ap,
std::unique_ptr<WavReader> in_file,
std::unique_ptr<WavWriter> out_file,
std::unique_ptr<WavReader> reverse_in_file,
std::unique_ptr<WavWriter> reverse_out_file);
virtual ~WavFileProcessor() {}
// Processes one chunk from the WAV input and writes to the WAV output.
bool ProcessChunk() override;
private:
std::unique_ptr<AudioProcessing> ap_;
ChannelBuffer<float> in_buf_;
ChannelBuffer<float> out_buf_;
const StreamConfig input_config_;
const StreamConfig output_config_;
ChannelBufferWavReader buffer_reader_;
ChannelBufferWavWriter buffer_writer_;
std::unique_ptr<ChannelBuffer<float>> reverse_in_buf_;
std::unique_ptr<ChannelBuffer<float>> reverse_out_buf_;
std::unique_ptr<StreamConfig> reverse_input_config_;
std::unique_ptr<StreamConfig> reverse_output_config_;
std::unique_ptr<ChannelBufferWavReader> reverse_buffer_reader_;
std::unique_ptr<ChannelBufferWavWriter> reverse_buffer_writer_;
};
// Used to read from an aecdump file and write to a WavWriter.
class AecDumpFileProcessor final : public AudioFileProcessor {
public:
// Takes ownership of all parameters.
AecDumpFileProcessor(std::unique_ptr<AudioProcessing> ap,
FILE* dump_file,
std::unique_ptr<WavWriter> out_file);
virtual ~AecDumpFileProcessor();
// Processes messages from the aecdump file until the first Stream message is
// completed. Passes other data from the aecdump messages as appropriate.
bool ProcessChunk() override;
private:
void HandleMessage(const webrtc::audioproc::Init& msg);
void HandleMessage(const webrtc::audioproc::Stream& msg);
void HandleMessage(const webrtc::audioproc::ReverseStream& msg);
std::unique_ptr<AudioProcessing> ap_;
FILE* dump_file_;
std::unique_ptr<ChannelBuffer<float>> in_buf_;
std::unique_ptr<ChannelBuffer<float>> reverse_buf_;
ChannelBuffer<float> out_buf_;
StreamConfig input_config_;
StreamConfig reverse_config_;
const StreamConfig output_config_;
ChannelBufferWavWriter buffer_writer_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AUDIO_FILE_PROCESSOR_H_

View File

@ -0,0 +1,334 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_processing/test/audio_processing_simulator.h"
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "webrtc/base/stringutils.h"
#include "webrtc/common_audio/include/audio_util.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
namespace webrtc {
namespace test {
namespace {
void CopyFromAudioFrame(const AudioFrame& src, ChannelBuffer<float>* dest) {
RTC_CHECK_EQ(src.num_channels_, dest->num_channels());
RTC_CHECK_EQ(src.samples_per_channel_, dest->num_frames());
// Copy the data from the input buffer.
std::vector<float> tmp(src.samples_per_channel_ * src.num_channels_);
S16ToFloat(src.data_, tmp.size(), tmp.data());
Deinterleave(tmp.data(), src.samples_per_channel_, src.num_channels_,
dest->channels());
}
std::string GetIndexedOutputWavFilename(const std::string& wav_name,
int counter) {
std::stringstream ss;
ss << wav_name.substr(0, wav_name.size() - 4) << "_" << counter
<< wav_name.substr(wav_name.size() - 4);
return ss.str();
}
} // namespace
void CopyToAudioFrame(const ChannelBuffer<float>& src, AudioFrame* dest) {
RTC_CHECK_EQ(src.num_channels(), dest->num_channels_);
RTC_CHECK_EQ(src.num_frames(), dest->samples_per_channel_);
for (size_t ch = 0; ch < dest->num_channels_; ++ch) {
for (size_t sample = 0; sample < dest->samples_per_channel_; ++sample) {
dest->data_[sample * dest->num_channels_ + ch] =
src.channels()[ch][sample] * 32767;
}
}
}
AudioProcessingSimulator::ScopedTimer::~ScopedTimer() {
int64_t interval = rtc::TimeNanos() - start_time_;
proc_time_->sum += interval;
proc_time_->max = std::max(proc_time_->max, interval);
proc_time_->min = std::min(proc_time_->min, interval);
}
void AudioProcessingSimulator::ProcessStream(bool fixed_interface) {
if (fixed_interface) {
{
const auto st = ScopedTimer(mutable_proc_time());
RTC_CHECK_EQ(AudioProcessing::kNoError, ap_->ProcessStream(&fwd_frame_));
}
CopyFromAudioFrame(fwd_frame_, out_buf_.get());
} else {
const auto st = ScopedTimer(mutable_proc_time());
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->ProcessStream(in_buf_->channels(), in_config_,
out_config_, out_buf_->channels()));
}
if (buffer_writer_) {
buffer_writer_->Write(*out_buf_);
}
++num_process_stream_calls_;
}
void AudioProcessingSimulator::ProcessReverseStream(bool fixed_interface) {
if (fixed_interface) {
const auto st = ScopedTimer(mutable_proc_time());
RTC_CHECK_EQ(AudioProcessing::kNoError, ap_->ProcessStream(&rev_frame_));
CopyFromAudioFrame(rev_frame_, reverse_out_buf_.get());
} else {
const auto st = ScopedTimer(mutable_proc_time());
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->ProcessReverseStream(
reverse_in_buf_->channels(), reverse_in_config_,
reverse_out_config_, reverse_out_buf_->channels()));
}
if (reverse_buffer_writer_) {
reverse_buffer_writer_->Write(*reverse_out_buf_);
}
++num_reverse_process_stream_calls_;
}
void AudioProcessingSimulator::SetupBuffersConfigsOutputs(
int input_sample_rate_hz,
int output_sample_rate_hz,
int reverse_input_sample_rate_hz,
int reverse_output_sample_rate_hz,
int input_num_channels,
int output_num_channels,
int reverse_input_num_channels,
int reverse_output_num_channels) {
in_config_ = StreamConfig(input_sample_rate_hz, input_num_channels);
in_buf_.reset(new ChannelBuffer<float>(
rtc::CheckedDivExact(input_sample_rate_hz, kChunksPerSecond),
input_num_channels));
reverse_in_config_ =
StreamConfig(reverse_input_sample_rate_hz, reverse_input_num_channels);
reverse_in_buf_.reset(new ChannelBuffer<float>(
rtc::CheckedDivExact(reverse_input_sample_rate_hz, kChunksPerSecond),
reverse_input_num_channels));
out_config_ = StreamConfig(output_sample_rate_hz, output_num_channels);
out_buf_.reset(new ChannelBuffer<float>(
rtc::CheckedDivExact(output_sample_rate_hz, kChunksPerSecond),
output_num_channels));
reverse_out_config_ =
StreamConfig(reverse_output_sample_rate_hz, reverse_output_num_channels);
reverse_out_buf_.reset(new ChannelBuffer<float>(
rtc::CheckedDivExact(reverse_output_sample_rate_hz, kChunksPerSecond),
reverse_output_num_channels));
fwd_frame_.sample_rate_hz_ = input_sample_rate_hz;
fwd_frame_.samples_per_channel_ =
rtc::CheckedDivExact(fwd_frame_.sample_rate_hz_, kChunksPerSecond);
fwd_frame_.num_channels_ = input_num_channels;
rev_frame_.sample_rate_hz_ = reverse_input_sample_rate_hz;
rev_frame_.samples_per_channel_ =
rtc::CheckedDivExact(rev_frame_.sample_rate_hz_, kChunksPerSecond);
rev_frame_.num_channels_ = reverse_input_num_channels;
if (settings_.use_verbose_logging) {
std::cout << "Sample rates:" << std::endl;
std::cout << " Forward input: " << input_sample_rate_hz << std::endl;
std::cout << " Forward output: " << output_sample_rate_hz << std::endl;
std::cout << " Reverse input: " << reverse_input_sample_rate_hz
<< std::endl;
std::cout << " Reverse output: " << reverse_output_sample_rate_hz
<< std::endl;
std::cout << "Number of channels: " << std::endl;
std::cout << " Forward input: " << input_num_channels << std::endl;
std::cout << " Forward output: " << output_num_channels << std::endl;
std::cout << " Reverse input: " << reverse_input_num_channels << std::endl;
std::cout << " Reverse output: " << reverse_output_num_channels
<< std::endl;
}
SetupOutput();
}
void AudioProcessingSimulator::SetupOutput() {
if (settings_.output_filename) {
std::string filename;
if (settings_.store_intermediate_output) {
filename = GetIndexedOutputWavFilename(*settings_.output_filename,
output_reset_counter_);
} else {
filename = *settings_.output_filename;
}
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)));
}
if (settings_.reverse_output_filename) {
std::string filename;
if (settings_.store_intermediate_output) {
filename = GetIndexedOutputWavFilename(*settings_.reverse_output_filename,
output_reset_counter_);
} else {
filename = *settings_.reverse_output_filename;
}
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(
new ChannelBufferWavWriter(std::move(reverse_out_file)));
}
++output_reset_counter_;
}
void AudioProcessingSimulator::DestroyAudioProcessor() {
if (settings_.aec_dump_output_filename) {
RTC_CHECK_EQ(AudioProcessing::kNoError, ap_->StopDebugRecording());
}
}
void AudioProcessingSimulator::CreateAudioProcessor() {
Config config;
if (settings_.use_bf && *settings_.use_bf) {
config.Set<Beamforming>(new Beamforming(
true, ParseArrayGeometry(*settings_.microphone_positions),
SphericalPointf(DegreesToRadians(settings_.target_angle_degrees), 0.f,
1.f)));
}
if (settings_.use_ts) {
config.Set<ExperimentalNs>(new ExperimentalNs(*settings_.use_ts));
}
if (settings_.use_ie) {
config.Set<Intelligibility>(new Intelligibility(*settings_.use_ie));
}
if (settings_.use_aec3) {
config.Set<EchoCanceller3>(new EchoCanceller3(*settings_.use_aec3));
}
if (settings_.use_refined_adaptive_filter) {
config.Set<RefinedAdaptiveFilter>(
new RefinedAdaptiveFilter(*settings_.use_refined_adaptive_filter));
}
config.Set<ExtendedFilter>(new ExtendedFilter(
!settings_.use_extended_filter || *settings_.use_extended_filter));
config.Set<DelayAgnostic>(new DelayAgnostic(!settings_.use_delay_agnostic ||
*settings_.use_delay_agnostic));
ap_.reset(AudioProcessing::Create(config));
if (settings_.use_aec) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_cancellation()->Enable(*settings_.use_aec));
}
if (settings_.use_aecm) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_control_mobile()->Enable(*settings_.use_aecm));
}
if (settings_.use_agc) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->gain_control()->Enable(*settings_.use_agc));
}
if (settings_.use_hpf) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->high_pass_filter()->Enable(*settings_.use_hpf));
}
if (settings_.use_ns) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->noise_suppression()->Enable(*settings_.use_ns));
}
if (settings_.use_le) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->level_estimator()->Enable(*settings_.use_le));
}
if (settings_.use_vad) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->voice_detection()->Enable(*settings_.use_vad));
}
if (settings_.use_agc_limiter) {
RTC_CHECK_EQ(AudioProcessing::kNoError, ap_->gain_control()->enable_limiter(
*settings_.use_agc_limiter));
}
if (settings_.agc_target_level) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->gain_control()->set_target_level_dbfs(
*settings_.agc_target_level));
}
if (settings_.agc_mode) {
RTC_CHECK_EQ(
AudioProcessing::kNoError,
ap_->gain_control()->set_mode(
static_cast<webrtc::GainControl::Mode>(*settings_.agc_mode)));
}
if (settings_.use_drift_compensation) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_cancellation()->enable_drift_compensation(
*settings_.use_drift_compensation));
}
if (settings_.aec_suppression_level) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_cancellation()->set_suppression_level(
static_cast<webrtc::EchoCancellation::SuppressionLevel>(
*settings_.aec_suppression_level)));
}
if (settings_.aecm_routing_mode) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_control_mobile()->set_routing_mode(
static_cast<webrtc::EchoControlMobile::RoutingMode>(
*settings_.aecm_routing_mode)));
}
if (settings_.use_aecm_comfort_noise) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->echo_control_mobile()->enable_comfort_noise(
*settings_.use_aecm_comfort_noise));
}
if (settings_.vad_likelihood) {
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->voice_detection()->set_likelihood(
static_cast<webrtc::VoiceDetection::Likelihood>(
*settings_.vad_likelihood)));
}
if (settings_.ns_level) {
RTC_CHECK_EQ(
AudioProcessing::kNoError,
ap_->noise_suppression()->set_level(
static_cast<NoiseSuppression::Level>(*settings_.ns_level)));
}
if (settings_.use_ts) {
ap_->set_stream_key_pressed(*settings_.use_ts);
}
if (settings_.aec_dump_output_filename) {
size_t kMaxFilenameSize = AudioProcessing::kMaxFilenameSize;
RTC_CHECK_LE(settings_.aec_dump_output_filename->size(), kMaxFilenameSize);
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->StartDebugRecording(
settings_.aec_dump_output_filename->c_str(), -1));
}
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,175 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AUDIO_PROCESSING_SIMULATOR_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AUDIO_PROCESSING_SIMULATOR_H_
#include <algorithm>
#include <limits>
#include <memory>
#include <string>
#include "webrtc/base/timeutils.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/optional.h"
#include "webrtc/common_audio/channel_buffer.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
namespace webrtc {
namespace test {
// Holds all the parameters available for controlling the simulation.
struct SimulationSettings {
rtc::Optional<int> stream_delay;
rtc::Optional<int> stream_drift_samples;
rtc::Optional<int> output_sample_rate_hz;
rtc::Optional<int> output_num_channels;
rtc::Optional<int> reverse_output_sample_rate_hz;
rtc::Optional<int> reverse_output_num_channels;
rtc::Optional<std::string> microphone_positions;
int target_angle_degrees = 90;
rtc::Optional<std::string> output_filename;
rtc::Optional<std::string> reverse_output_filename;
rtc::Optional<std::string> input_filename;
rtc::Optional<std::string> reverse_input_filename;
rtc::Optional<bool> use_aec;
rtc::Optional<bool> use_aecm;
rtc::Optional<bool> use_agc;
rtc::Optional<bool> use_hpf;
rtc::Optional<bool> use_ns;
rtc::Optional<bool> use_ts;
rtc::Optional<bool> use_bf;
rtc::Optional<bool> use_ie;
rtc::Optional<bool> use_vad;
rtc::Optional<bool> use_le;
rtc::Optional<bool> use_all;
rtc::Optional<int> aec_suppression_level;
rtc::Optional<bool> use_delay_agnostic;
rtc::Optional<bool> use_extended_filter;
rtc::Optional<bool> use_drift_compensation;
rtc::Optional<bool> use_aec3;
rtc::Optional<int> aecm_routing_mode;
rtc::Optional<bool> use_aecm_comfort_noise;
rtc::Optional<int> agc_mode;
rtc::Optional<int> agc_target_level;
rtc::Optional<bool> use_agc_limiter;
rtc::Optional<int> agc_compression_gain;
rtc::Optional<int> vad_likelihood;
rtc::Optional<int> ns_level;
rtc::Optional<bool> use_refined_adaptive_filter;
bool report_performance = false;
bool report_bitexactness = false;
bool use_verbose_logging = false;
bool discard_all_settings_in_aecdump = true;
rtc::Optional<std::string> aec_dump_input_filename;
rtc::Optional<std::string> aec_dump_output_filename;
bool fixed_interface = false;
bool store_intermediate_output = false;
};
// Holds a few statistics about a series of TickIntervals.
struct TickIntervalStats {
TickIntervalStats() : min(std::numeric_limits<int64_t>::max()) {}
int64_t sum;
int64_t max;
int64_t min;
};
// Copies samples present in a ChannelBuffer into an AudioFrame.
void CopyToAudioFrame(const ChannelBuffer<float>& src, AudioFrame* dest);
// Provides common functionality for performing audioprocessing simulations.
class AudioProcessingSimulator {
public:
static const int kChunksPerSecond = 1000 / AudioProcessing::kChunkSizeMs;
explicit AudioProcessingSimulator(const SimulationSettings& settings)
: settings_(settings) {}
virtual ~AudioProcessingSimulator() {}
// Processes the data in the input.
virtual void Process() = 0;
// Returns the execution time of all AudioProcessing calls.
const TickIntervalStats& proc_time() const { return proc_time_; }
// Reports whether the processed recording was bitexact.
bool OutputWasBitexact() { return bitexact_output_; }
size_t get_num_process_stream_calls() { return num_process_stream_calls_; }
size_t get_num_reverse_process_stream_calls() {
return num_reverse_process_stream_calls_;
}
protected:
// RAII class for execution time measurement. Updates the provided
// TickIntervalStats based on the time between ScopedTimer creation and
// leaving the enclosing scope.
class ScopedTimer {
public:
explicit ScopedTimer(TickIntervalStats* proc_time)
: proc_time_(proc_time), start_time_(rtc::TimeNanos()) {}
~ScopedTimer();
private:
TickIntervalStats* const proc_time_;
int64_t start_time_;
};
TickIntervalStats* mutable_proc_time() { return &proc_time_; }
void ProcessStream(bool fixed_interface);
void ProcessReverseStream(bool fixed_interface);
void CreateAudioProcessor();
void DestroyAudioProcessor();
void SetupBuffersConfigsOutputs(int input_sample_rate_hz,
int output_sample_rate_hz,
int reverse_input_sample_rate_hz,
int reverse_output_sample_rate_hz,
int input_num_channels,
int output_num_channels,
int reverse_input_num_channels,
int reverse_output_num_channels);
const SimulationSettings settings_;
std::unique_ptr<AudioProcessing> ap_;
std::unique_ptr<ChannelBuffer<float>> in_buf_;
std::unique_ptr<ChannelBuffer<float>> out_buf_;
std::unique_ptr<ChannelBuffer<float>> reverse_in_buf_;
std::unique_ptr<ChannelBuffer<float>> reverse_out_buf_;
StreamConfig in_config_;
StreamConfig out_config_;
StreamConfig reverse_in_config_;
StreamConfig reverse_out_config_;
std::unique_ptr<ChannelBufferWavReader> buffer_reader_;
std::unique_ptr<ChannelBufferWavReader> reverse_buffer_reader_;
AudioFrame rev_frame_;
AudioFrame fwd_frame_;
bool bitexact_output_ = true;
private:
void SetupOutput();
size_t num_process_stream_calls_ = 0;
size_t num_reverse_process_stream_calls_ = 0;
size_t output_reset_counter_ = 0;
std::unique_ptr<ChannelBufferWavWriter> buffer_writer_;
std::unique_ptr<ChannelBufferWavWriter> reverse_buffer_writer_;
TickIntervalStats proc_time_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(AudioProcessingSimulator);
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_AUDIO_PROCESSING_SIMULATOR_H_

View File

@ -8,179 +8,425 @@
* be found in the AUTHORS file in the root of the source tree.
*/
#include <stdio.h>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include <string.h>
#include "gflags/gflags.h"
#include "webrtc/base/checks.h"
#include "webrtc/base/format_macros.h"
#include "webrtc/common_audio/channel_buffer.h"
#include "webrtc/common_audio/wav_file.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/modules/audio_processing/test/audio_file_processor.h"
#include "webrtc/modules/audio_processing/test/protobuf_utils.h"
#include "webrtc/modules/audio_processing/test/test_utils.h"
#include "webrtc/test/testsupport/trace_to_stderr.h"
#include "webrtc/modules/audio_processing/test/aec_dump_based_simulator.h"
#include "webrtc/modules/audio_processing/test/audio_processing_simulator.h"
#include "webrtc/modules/audio_processing/test/wav_based_simulator.h"
namespace webrtc {
namespace test {
namespace {
bool ValidateOutChannels(const char* flagname, int32_t value) {
return value >= 0;
const int kParameterNotSpecifiedValue = -10000;
const char kUsageDescription[] =
"Usage: audioproc_f [options] -i <input.wav>\n"
" or\n"
" audioproc_f [options] -dump_input <aec_dump>\n"
"\n\n"
"Command-line tool to simulate a call using the audio "
"processing module, either based on wav files or "
"protobuf debug dump recordings.";
DEFINE_string(dump_input, "", "Aec dump input filename");
DEFINE_string(dump_output, "", "Aec dump output filename");
DEFINE_string(i, "", "Forward stream input wav filename");
DEFINE_string(o, "", "Forward stream output wav filename");
DEFINE_string(ri, "", "Reverse stream input wav filename");
DEFINE_string(ro, "", "Reverse stream output wav filename");
DEFINE_int32(output_num_channels,
kParameterNotSpecifiedValue,
"Number of forward stream output channels");
DEFINE_int32(reverse_output_num_channels,
kParameterNotSpecifiedValue,
"Number of Reverse stream output channels");
DEFINE_int32(output_sample_rate_hz,
kParameterNotSpecifiedValue,
"Forward stream output sample rate in Hz");
DEFINE_int32(reverse_output_sample_rate_hz,
kParameterNotSpecifiedValue,
"Reverse stream output sample rate in Hz");
DEFINE_string(mic_positions,
"",
"Space delimited cartesian coordinates of microphones in "
"meters. The coordinates of each point are contiguous. For a "
"two element array: \"x1 y1 z1 x2 y2 z2\"");
DEFINE_int32(target_angle_degrees,
90,
"The azimuth of the target in degrees (0-359). Only applies to "
"beamforming.");
DEFINE_bool(fixed_interface,
false,
"Use the fixed interface when operating on wav files");
DEFINE_int32(aec,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the echo canceller");
DEFINE_int32(aecm,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the mobile echo controller");
DEFINE_int32(agc,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the AGC");
DEFINE_int32(hpf,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the high-pass filter");
DEFINE_int32(ns,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the noise suppressor");
DEFINE_int32(ts,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the transient suppressor");
DEFINE_int32(bf,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the beamformer");
DEFINE_int32(ie,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the intelligibility enhancer");
DEFINE_int32(vad,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the voice activity detector");
DEFINE_int32(le,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the level estimator");
DEFINE_bool(all_default,
false,
"Activate all of the default components (will be overridden by any "
"other settings)");
DEFINE_int32(aec_suppression_level,
kParameterNotSpecifiedValue,
"Set the aec suppression level (0-2)");
DEFINE_int32(delay_agnostic,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the AEC delay agnostic mode");
DEFINE_int32(extended_filter,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the AEC extended filter mode");
DEFINE_int32(drift_compensation,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the drift compensation");
DEFINE_int32(aec3,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the experimental AEC mode AEC3");
DEFINE_int32(
refined_adaptive_filter,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the refined adaptive filter functionality");
DEFINE_int32(aecm_routing_mode,
kParameterNotSpecifiedValue,
"Specify the AECM routing mode (0-4)");
DEFINE_int32(aecm_comfort_noise,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the AECM comfort noise");
DEFINE_int32(agc_mode,
kParameterNotSpecifiedValue,
"Specify the AGC mode (0-2)");
DEFINE_int32(agc_target_level,
kParameterNotSpecifiedValue,
"Specify the AGC target level (0-31)");
DEFINE_int32(agc_limiter,
kParameterNotSpecifiedValue,
"Activate (1) or deactivate(0) the level estimator");
DEFINE_int32(agc_compression_gain,
kParameterNotSpecifiedValue,
"Specify the AGC compression gain (0-90)");
DEFINE_int32(vad_likelihood,
kParameterNotSpecifiedValue,
"Specify the VAD likelihood (0-3)");
DEFINE_int32(ns_level,
kParameterNotSpecifiedValue,
"Specify the NS level (0-3)");
DEFINE_int32(stream_delay,
kParameterNotSpecifiedValue,
"Specify the stream delay in ms to use");
DEFINE_int32(stream_drift_samples,
kParameterNotSpecifiedValue,
"Specify the number of stream drift samples to use");
DEFINE_bool(performance_report, false, "Report the APM performance ");
DEFINE_bool(verbose, false, "Produce verbose output");
DEFINE_bool(bitexactness_report,
false,
"Report bitexactness for aec dump result reproduction");
DEFINE_bool(discard_settings_in_aecdump,
false,
"Discard any config settings specified in the aec dump");
DEFINE_bool(store_intermediate_output,
false,
"Creates new output files after each init");
void SetSettingIfSpecified(const std::string value,
rtc::Optional<std::string>* parameter) {
if (value.compare("") != 0) {
*parameter = rtc::Optional<std::string>(value);
}
}
void SetSettingIfSpecified(int value, rtc::Optional<int>* parameter) {
if (value != kParameterNotSpecifiedValue) {
*parameter = rtc::Optional<int>(value);
}
}
void SetSettingIfFlagSet(int32_t flag, rtc::Optional<bool>* parameter) {
if (flag == 0) {
*parameter = rtc::Optional<bool>(false);
} else if (flag == 1) {
*parameter = rtc::Optional<bool>(true);
}
}
SimulationSettings CreateSettings() {
SimulationSettings settings;
if (FLAGS_all_default) {
settings.use_le = rtc::Optional<bool>(true);
settings.use_vad = rtc::Optional<bool>(true);
settings.use_ie = rtc::Optional<bool>(false);
settings.use_bf = rtc::Optional<bool>(false);
settings.use_ts = rtc::Optional<bool>(true);
settings.use_ns = rtc::Optional<bool>(true);
settings.use_hpf = rtc::Optional<bool>(true);
settings.use_agc = rtc::Optional<bool>(true);
settings.use_aec = rtc::Optional<bool>(true);
settings.use_aecm = rtc::Optional<bool>(false);
}
SetSettingIfSpecified(FLAGS_dump_input, &settings.aec_dump_input_filename);
SetSettingIfSpecified(FLAGS_dump_output, &settings.aec_dump_output_filename);
SetSettingIfSpecified(FLAGS_i, &settings.input_filename);
SetSettingIfSpecified(FLAGS_o, &settings.output_filename);
SetSettingIfSpecified(FLAGS_ri, &settings.reverse_input_filename);
SetSettingIfSpecified(FLAGS_ro, &settings.reverse_output_filename);
SetSettingIfSpecified(FLAGS_output_num_channels,
&settings.output_num_channels);
SetSettingIfSpecified(FLAGS_reverse_output_num_channels,
&settings.reverse_output_num_channels);
SetSettingIfSpecified(FLAGS_output_sample_rate_hz,
&settings.output_sample_rate_hz);
SetSettingIfSpecified(FLAGS_reverse_output_sample_rate_hz,
&settings.reverse_output_sample_rate_hz);
SetSettingIfSpecified(FLAGS_mic_positions, &settings.microphone_positions);
settings.target_angle_degrees = FLAGS_target_angle_degrees;
SetSettingIfFlagSet(FLAGS_aec, &settings.use_aec);
SetSettingIfFlagSet(FLAGS_aecm, &settings.use_aecm);
SetSettingIfFlagSet(FLAGS_agc, &settings.use_agc);
SetSettingIfFlagSet(FLAGS_hpf, &settings.use_hpf);
SetSettingIfFlagSet(FLAGS_ns, &settings.use_ns);
SetSettingIfFlagSet(FLAGS_ts, &settings.use_ts);
SetSettingIfFlagSet(FLAGS_bf, &settings.use_bf);
SetSettingIfFlagSet(FLAGS_ie, &settings.use_ie);
SetSettingIfFlagSet(FLAGS_vad, &settings.use_vad);
SetSettingIfFlagSet(FLAGS_le, &settings.use_le);
SetSettingIfSpecified(FLAGS_aec_suppression_level,
&settings.aec_suppression_level);
SetSettingIfFlagSet(FLAGS_delay_agnostic, &settings.use_delay_agnostic);
SetSettingIfFlagSet(FLAGS_extended_filter, &settings.use_extended_filter);
SetSettingIfFlagSet(FLAGS_drift_compensation,
&settings.use_drift_compensation);
SetSettingIfFlagSet(FLAGS_refined_adaptive_filter,
&settings.use_refined_adaptive_filter);
SetSettingIfFlagSet(FLAGS_aec3, &settings.use_aec3);
SetSettingIfSpecified(FLAGS_aecm_routing_mode, &settings.aecm_routing_mode);
SetSettingIfFlagSet(FLAGS_aecm_comfort_noise,
&settings.use_aecm_comfort_noise);
SetSettingIfSpecified(FLAGS_agc_mode, &settings.agc_mode);
SetSettingIfSpecified(FLAGS_agc_target_level, &settings.agc_target_level);
SetSettingIfFlagSet(FLAGS_agc_limiter, &settings.use_agc_limiter);
SetSettingIfSpecified(FLAGS_agc_compression_gain,
&settings.agc_compression_gain);
SetSettingIfSpecified(FLAGS_vad_likelihood, &settings.vad_likelihood);
SetSettingIfSpecified(FLAGS_ns_level, &settings.ns_level);
SetSettingIfSpecified(FLAGS_stream_delay, &settings.stream_delay);
SetSettingIfSpecified(FLAGS_stream_drift_samples,
&settings.stream_drift_samples);
settings.report_performance = FLAGS_performance_report;
settings.use_verbose_logging = FLAGS_verbose;
settings.report_bitexactness = FLAGS_bitexactness_report;
settings.discard_all_settings_in_aecdump = FLAGS_discard_settings_in_aecdump;
settings.fixed_interface = FLAGS_fixed_interface;
settings.store_intermediate_output = FLAGS_store_intermediate_output;
return settings;
}
void ReportConditionalErrorAndExit(bool condition, std::string message) {
if (condition) {
std::cerr << message << std::endl;
exit(1);
}
}
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.input_filename,
"Error: When operating at wav files, the "
"input wav filename must be "
"specified!\n");
ReportConditionalErrorAndExit(
settings.reverse_output_filename && !settings.reverse_input_filename,
"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.use_aec && *settings.use_aec && settings.use_aecm &&
*settings.use_aecm,
"Error: The AEC and the AECM cannot be activated at the same time!\n");
ReportConditionalErrorAndExit(
settings.output_sample_rate_hz && *settings.output_sample_rate_hz <= 0,
"Error: --output_sample_rate_hz must be positive!\n");
ReportConditionalErrorAndExit(
settings.reverse_output_sample_rate_hz &&
settings.output_sample_rate_hz &&
*settings.output_sample_rate_hz <= 0,
"Error: --reverse_output_sample_rate_hz must be positive!\n");
ReportConditionalErrorAndExit(
settings.output_num_channels && *settings.output_num_channels <= 0,
"Error: --output_num_channels must be positive!\n");
ReportConditionalErrorAndExit(
settings.reverse_output_num_channels &&
*settings.reverse_output_num_channels <= 0,
"Error: --reverse_output_num_channels must be positive!\n");
ReportConditionalErrorAndExit(
settings.use_bf && *settings.use_bf && !settings.microphone_positions,
"Error: --mic_positions must be specified when the beamformer is "
"activated.\n");
ReportConditionalErrorAndExit(
settings.target_angle_degrees < 0 || settings.target_angle_degrees > 359,
"Error: -target_angle_degrees must be specified between 0 and 359.\n");
ReportConditionalErrorAndExit(
settings.aec_suppression_level &&
((*settings.aec_suppression_level) < 0 ||
(*settings.aec_suppression_level) > 2),
"Error: --aec_suppression_level must be specified between 0 and 2.\n");
ReportConditionalErrorAndExit(
settings.aecm_routing_mode && ((*settings.aecm_routing_mode) < 0 ||
(*settings.aecm_routing_mode) > 4),
"Error: --aecm_routing_mode must be specified between 0 and 4.\n");
ReportConditionalErrorAndExit(
settings.agc_target_level && ((*settings.agc_target_level) < 0 ||
(*settings.agc_target_level) > 31),
"Error: --agc_target_level must be specified between 0 and 31.\n");
ReportConditionalErrorAndExit(
settings.agc_compression_gain && ((*settings.agc_compression_gain) < 0 ||
(*settings.agc_compression_gain) > 90),
"Error: --agc_compression_gain must be specified between 0 and 90.\n");
ReportConditionalErrorAndExit(
settings.vad_likelihood &&
((*settings.vad_likelihood) < 0 || (*settings.vad_likelihood) > 3),
"Error: --vad_likelihood must be specified between 0 and 3.\n");
ReportConditionalErrorAndExit(
settings.ns_level &&
((*settings.ns_level) < 0 || (*settings.ns_level) > 3),
"Error: --ns_level must be specified between 0 and 3.\n");
ReportConditionalErrorAndExit(
settings.report_bitexactness && !settings.aec_dump_input_filename,
"Error: --bitexactness_report can only be used when operating on an "
"aecdump\n");
auto valid_wav_name = [](const std::string& wav_file_name) {
if (wav_file_name.size() < 5) {
return false;
}
if ((wav_file_name.compare(wav_file_name.size() - 4, 4, ".wav") == 0) ||
(wav_file_name.compare(wav_file_name.size() - 4, 4, ".WAV") == 0)) {
return true;
}
return false;
};
ReportConditionalErrorAndExit(
settings.input_filename && (!valid_wav_name(*settings.input_filename)),
"Error: --i must be a valid .wav file name.\n");
ReportConditionalErrorAndExit(
settings.output_filename && (!valid_wav_name(*settings.output_filename)),
"Error: --o must be a valid .wav file name.\n");
ReportConditionalErrorAndExit(
settings.reverse_input_filename &&
(!valid_wav_name(*settings.reverse_input_filename)),
"Error: --ri must be a valid .wav file name.\n");
ReportConditionalErrorAndExit(
settings.reverse_output_filename &&
(!valid_wav_name(*settings.reverse_output_filename)),
"Error: --ro must be a valid .wav file name.\n");
}
} // namespace
DEFINE_string(dump, "", "Name of the aecdump debug file to read from.");
DEFINE_string(i, "", "Name of the capture input stream file to read from.");
DEFINE_string(
o,
"out.wav",
"Name of the output file to write the processed capture stream to.");
DEFINE_string(ri, "", "Name of the render input stream file to read from.");
DEFINE_string(
ro,
"out_reverse.wav",
"Name of the output file to write the processed render stream to.");
DEFINE_int32(out_channels, 1, "Number of output channels.");
const bool out_channels_dummy =
google::RegisterFlagValidator(&FLAGS_out_channels, &ValidateOutChannels);
DEFINE_int32(rev_out_channels, 1, "Number of reverse output channels.");
const bool rev_out_channels_dummy =
google::RegisterFlagValidator(&FLAGS_rev_out_channels,
&ValidateOutChannels);
DEFINE_int32(out_sample_rate, 48000, "Output sample rate in Hz.");
DEFINE_int32(rev_out_sample_rate, 48000, "Reverse output sample rate in Hz.");
DEFINE_string(mic_positions, "",
"Space delimited cartesian coordinates of microphones in meters. "
"The coordinates of each point are contiguous. "
"For a two element array: \"x1 y1 z1 x2 y2 z2\"");
DEFINE_double(
target_angle_degrees,
90,
"The azimuth of the target in degrees. Only applies to beamforming.");
DEFINE_bool(aec, false, "Enable echo cancellation.");
DEFINE_bool(agc, false, "Enable automatic gain control.");
DEFINE_bool(hpf, false, "Enable high-pass filtering.");
DEFINE_bool(ns, false, "Enable noise suppression.");
DEFINE_bool(ts, false, "Enable transient suppression.");
DEFINE_bool(bf, false, "Enable beamforming.");
DEFINE_bool(ie, false, "Enable intelligibility enhancer.");
DEFINE_bool(all, false, "Enable all components.");
DEFINE_int32(ns_level, -1, "Noise suppression level [0 - 3].");
DEFINE_bool(perf, false, "Enable performance tests.");
namespace webrtc {
namespace {
const int kChunksPerSecond = 100;
const char kUsage[] =
"Command-line tool to run audio processing on WAV files. Accepts either\n"
"an input capture WAV file or protobuf debug dump and writes to an output\n"
"WAV file.\n"
"\n"
"All components are disabled by default.";
} // namespace
int main(int argc, char* argv[]) {
google::SetUsageMessage(kUsage);
google::SetUsageMessage(kUsageDescription);
google::ParseCommandLineFlags(&argc, &argv, true);
if (!((FLAGS_i.empty()) ^ (FLAGS_dump.empty()))) {
fprintf(stderr,
"An input file must be specified with either -i or -dump.\n");
return 1;
}
test::TraceToStderr trace_to_stderr(true);
Config config;
if (FLAGS_bf || FLAGS_all) {
if (FLAGS_mic_positions.empty()) {
fprintf(stderr, "-mic_positions must be specified when -bf is used.\n");
return 1;
}
config.Set<Beamforming>(new Beamforming(
true, ParseArrayGeometry(FLAGS_mic_positions),
SphericalPointf(DegreesToRadians(FLAGS_target_angle_degrees), 0.f,
1.f)));
}
config.Set<ExperimentalNs>(new ExperimentalNs(FLAGS_ts || FLAGS_all));
config.Set<Intelligibility>(new Intelligibility(FLAGS_ie || FLAGS_all));
std::unique_ptr<AudioProcessing> ap(AudioProcessing::Create(config));
RTC_CHECK_EQ(kNoErr, ap->echo_cancellation()->Enable(FLAGS_aec || FLAGS_all));
RTC_CHECK_EQ(kNoErr, ap->gain_control()->Enable(FLAGS_agc || FLAGS_all));
RTC_CHECK_EQ(kNoErr, ap->high_pass_filter()->Enable(FLAGS_hpf || FLAGS_all));
RTC_CHECK_EQ(kNoErr, ap->noise_suppression()->Enable(FLAGS_ns || FLAGS_all));
if (FLAGS_ns_level != -1) {
RTC_CHECK_EQ(kNoErr,
ap->noise_suppression()->set_level(
static_cast<NoiseSuppression::Level>(FLAGS_ns_level)));
}
ap->set_stream_key_pressed(FLAGS_ts);
std::unique_ptr<AudioFileProcessor> processor;
auto out_file = std::unique_ptr<WavWriter>(new WavWriter(
FLAGS_o, FLAGS_out_sample_rate, static_cast<size_t>(FLAGS_out_channels)));
std::cout << FLAGS_o << ": " << out_file->FormatAsString() << std::endl;
if (FLAGS_dump.empty()) {
auto in_file = std::unique_ptr<WavReader>(new WavReader(FLAGS_i));
std::cout << FLAGS_i << ": " << in_file->FormatAsString() << std::endl;
std::unique_ptr<WavReader> reverse_in_file;
std::unique_ptr<WavWriter> reverse_out_file;
if (!FLAGS_ri.empty()) {
reverse_in_file.reset(new WavReader(FLAGS_ri));
reverse_out_file.reset(new WavWriter(
FLAGS_ro,
FLAGS_rev_out_sample_rate,
static_cast<size_t>(FLAGS_rev_out_channels)));
std::cout << FLAGS_ri << ": "
<< reverse_in_file->FormatAsString() << std::endl;
std::cout << FLAGS_ro << ": "
<< reverse_out_file->FormatAsString() << std::endl;
}
processor.reset(new WavFileProcessor(std::move(ap),
std::move(in_file),
std::move(out_file),
std::move(reverse_in_file),
std::move(reverse_out_file)));
SimulationSettings settings = CreateSettings();
PerformBasicParameterSanityChecks(settings);
std::unique_ptr<AudioProcessingSimulator> processor;
if (settings.aec_dump_input_filename) {
processor.reset(new AecDumpBasedSimulator(settings));
} else {
processor.reset(new AecDumpFileProcessor(
std::move(ap), fopen(FLAGS_dump.c_str(), "rb"), std::move(out_file)));
processor.reset(new WavBasedSimulator(settings));
}
int num_chunks = 0;
while (processor->ProcessChunk()) {
trace_to_stderr.SetTimeSeconds(num_chunks * 1.f / kChunksPerSecond);
++num_chunks;
}
processor->Process();
if (FLAGS_perf) {
if (settings.report_performance) {
const auto& proc_time = processor->proc_time();
int64_t exec_time_us = proc_time.sum / rtc::kNumNanosecsPerMicrosec;
printf(
"\nExecution time: %.3f s, File time: %.2f s\n"
"Time per chunk (mean, max, min):\n%.0f us, %.0f us, %.0f us\n",
exec_time_us * 1e-6, num_chunks * 1.f / kChunksPerSecond,
exec_time_us * 1.f / num_chunks,
1.f * proc_time.max / rtc::kNumNanosecsPerMicrosec,
1.f * proc_time.min / rtc::kNumNanosecsPerMicrosec);
std::cout << std::endl
<< "Execution time: " << exec_time_us * 1e-6 << " s, File time: "
<< processor->get_num_process_stream_calls() * 1.f /
AudioProcessingSimulator::kChunksPerSecond
<< std::endl
<< "Time per fwd stream chunk (mean, max, min): " << std::endl
<< exec_time_us * 1.f / processor->get_num_process_stream_calls()
<< " us, " << 1.f * proc_time.max / rtc::kNumNanosecsPerMicrosec
<< " us, " << 1.f * proc_time.min / rtc::kNumNanosecsPerMicrosec
<< " us" << std::endl;
}
if (settings.report_bitexactness && settings.aec_dump_input_filename) {
if (processor->OutputWasBitexact()) {
std::cout << "The processing was bitexact.";
} else {
std::cout << "The processing was not bitexact.";
}
}
return 0;
}
} // namespace test
} // namespace webrtc
int main(int argc, char* argv[]) {
return webrtc::main(argc, argv);
return webrtc::test::main(argc, argv);
}

View File

@ -709,9 +709,6 @@ void void_main(int argc, char* argv[]) {
const Stream msg = event_msg.stream();
primary_count++;
// ProcessStream could have changed this for the output frame.
near_frame.num_channels_ = apm->num_input_channels();
ASSERT_TRUE(msg.has_input_data() ^ (msg.input_channel_size() > 0));
if (msg.has_input_data()) {
ASSERT_EQ(sizeof(int16_t) * samples_per_channel *

View File

@ -0,0 +1,156 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_processing/test/wav_based_simulator.h"
#include "webrtc/base/checks.h"
#include "webrtc/test/testsupport/trace_to_stderr.h"
namespace webrtc {
namespace test {
std::vector<WavBasedSimulator::SimulationEventType>
WavBasedSimulator::GetDefaultEventChain() const {
std::vector<WavBasedSimulator::SimulationEventType> call_chain(2);
call_chain[0] = SimulationEventType::kProcessStream;
call_chain[1] = SimulationEventType::kProcessReverseStream;
return call_chain;
}
void WavBasedSimulator::PrepareProcessStreamCall() {
if (settings_.fixed_interface) {
CopyToAudioFrame(*in_buf_, &fwd_frame_);
}
ap_->set_stream_key_pressed(settings_.use_ts && (*settings_.use_ts));
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->set_stream_delay_ms(
settings_.stream_delay ? *settings_.stream_delay : 0));
ap_->echo_cancellation()->set_stream_drift_samples(
settings_.stream_drift_samples ? *settings_.stream_drift_samples : 0);
RTC_CHECK_EQ(AudioProcessing::kNoError,
ap_->gain_control()->set_stream_analog_level(
last_specified_microphone_level_));
}
void WavBasedSimulator::PrepareReverseProcessStreamCall() {
if (settings_.fixed_interface) {
CopyToAudioFrame(*reverse_in_buf_, &rev_frame_);
}
}
void WavBasedSimulator::Process() {
std::unique_ptr<test::TraceToStderr> trace_to_stderr;
if (settings_.use_verbose_logging) {
trace_to_stderr.reset(new test::TraceToStderr(true));
}
call_chain_ = GetDefaultEventChain();
CreateAudioProcessor();
Initialize();
bool samples_left_to_process = true;
int call_chain_index = 0;
int num_forward_chunks_processed = 0;
const int kOneBykChunksPerSecond =
1.f / AudioProcessingSimulator::kChunksPerSecond;
while (samples_left_to_process) {
switch (call_chain_[call_chain_index]) {
case SimulationEventType::kProcessStream:
samples_left_to_process = HandleProcessStreamCall();
++num_forward_chunks_processed;
break;
case SimulationEventType::kProcessReverseStream:
if (settings_.reverse_input_filename) {
samples_left_to_process = HandleProcessReverseStreamCall();
}
break;
default:
RTC_CHECK(false);
}
call_chain_index = (call_chain_index + 1) % call_chain_.size();
if (trace_to_stderr) {
trace_to_stderr->SetTimeSeconds(num_forward_chunks_processed *
kOneBykChunksPerSecond);
}
}
DestroyAudioProcessor();
}
bool WavBasedSimulator::HandleProcessStreamCall() {
bool samples_left_to_process = buffer_reader_->Read(in_buf_.get());
if (samples_left_to_process) {
PrepareProcessStreamCall();
ProcessStream(settings_.fixed_interface);
last_specified_microphone_level_ =
ap_->gain_control()->stream_analog_level();
}
return samples_left_to_process;
}
bool WavBasedSimulator::HandleProcessReverseStreamCall() {
bool samples_left_to_process =
reverse_buffer_reader_->Read(reverse_in_buf_.get());
if (samples_left_to_process) {
PrepareReverseProcessStreamCall();
ProcessReverseStream(settings_.fixed_interface);
}
return samples_left_to_process;
}
void WavBasedSimulator::Initialize() {
std::unique_ptr<WavReader> in_file(
new WavReader(settings_.input_filename->c_str()));
int input_sample_rate_hz = in_file->sample_rate();
int input_num_channels = in_file->num_channels();
buffer_reader_.reset(new ChannelBufferWavReader(std::move(in_file)));
int output_sample_rate_hz = settings_.output_sample_rate_hz
? *settings_.output_sample_rate_hz
: input_sample_rate_hz;
int output_num_channels = settings_.output_num_channels
? *settings_.output_num_channels
: input_num_channels;
int reverse_sample_rate_hz = 48000;
int reverse_num_channels = 1;
int reverse_output_sample_rate_hz = 48000;
int reverse_output_num_channels = 1;
if (settings_.reverse_input_filename) {
std::unique_ptr<WavReader> reverse_in_file(
new WavReader(settings_.reverse_input_filename->c_str()));
reverse_sample_rate_hz = reverse_in_file->sample_rate();
reverse_num_channels = reverse_in_file->num_channels();
reverse_buffer_reader_.reset(
new ChannelBufferWavReader(std::move(reverse_in_file)));
reverse_output_sample_rate_hz =
settings_.reverse_output_sample_rate_hz
? *settings_.reverse_output_sample_rate_hz
: reverse_sample_rate_hz;
reverse_output_num_channels = settings_.reverse_output_num_channels
? *settings_.reverse_output_num_channels
: reverse_num_channels;
}
SetupBuffersConfigsOutputs(
input_sample_rate_hz, output_sample_rate_hz, reverse_sample_rate_hz,
reverse_output_sample_rate_hz, input_num_channels, output_num_channels,
reverse_num_channels, reverse_output_num_channels);
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_AUDIO_PROCESSING_TEST_WAV_BASED_SIMULATOR_H_
#define WEBRTC_MODULES_AUDIO_PROCESSING_TEST_WAV_BASED_SIMULATOR_H_
#include <vector>
#include "webrtc/modules/audio_processing/test/audio_processing_simulator.h"
#include "webrtc/base/constructormagic.h"
namespace webrtc {
namespace test {
// Used to perform an audio processing simulation from wav files.
class WavBasedSimulator final : public AudioProcessingSimulator {
public:
explicit WavBasedSimulator(const SimulationSettings& settings)
: AudioProcessingSimulator(settings) {}
virtual ~WavBasedSimulator() {}
// Processes the WAV input.
void Process() override;
private:
enum SimulationEventType {
kProcessStream,
kProcessReverseStream,
};
void Initialize();
bool HandleProcessStreamCall();
bool HandleProcessReverseStreamCall();
void PrepareProcessStreamCall();
void PrepareReverseProcessStreamCall();
std::vector<SimulationEventType> GetDefaultEventChain() const;
std::vector<SimulationEventType> call_chain_;
int last_specified_microphone_level_ = 100;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(WavBasedSimulator);
};
} // namespace test
} // namespace webrtc
#endif // WEBRTC_MODULES_AUDIO_PROCESSING_TEST_WAV_BASED_SIMULATOR_H_