mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00
Allow extracting the linear AEC output
This CL enables extracting the linear AEC output, allowing for more straightforward testing/development. Bug: b/140823178 Change-Id: I14f7934008d87066b35500466cb6e6d96f811688 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/153672 Commit-Queue: Per Åhgren <peah@webrtc.org> Reviewed-by: Gustaf Ullberg <gustaf@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29789}
This commit is contained in:
parent
06a394e036
commit
c20a19cc4b
26 changed files with 293 additions and 90 deletions
|
@ -79,6 +79,7 @@ struct RTC_EXPORT EchoCanceller3Config {
|
||||||
bool conservative_initial_phase = false;
|
bool conservative_initial_phase = false;
|
||||||
bool enable_shadow_filter_output_usage = true;
|
bool enable_shadow_filter_output_usage = true;
|
||||||
bool use_linear_filter = true;
|
bool use_linear_filter = true;
|
||||||
|
bool export_linear_aec_output = false;
|
||||||
} filter;
|
} filter;
|
||||||
|
|
||||||
struct Erle {
|
struct Erle {
|
||||||
|
@ -185,8 +186,6 @@ struct RTC_EXPORT EchoCanceller3Config {
|
||||||
} high_bands_suppression;
|
} high_bands_suppression;
|
||||||
|
|
||||||
float floor_first_increase = 0.00001f;
|
float floor_first_increase = 0.00001f;
|
||||||
bool enforce_transparent = false;
|
|
||||||
bool enforce_empty_higher_bands = false;
|
|
||||||
} suppressor;
|
} suppressor;
|
||||||
};
|
};
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
|
@ -190,6 +190,8 @@ void Aec3ConfigFromJsonString(absl::string_view json_string,
|
||||||
ReadParam(section, "enable_shadow_filter_output_usage",
|
ReadParam(section, "enable_shadow_filter_output_usage",
|
||||||
&cfg.filter.enable_shadow_filter_output_usage);
|
&cfg.filter.enable_shadow_filter_output_usage);
|
||||||
ReadParam(section, "use_linear_filter", &cfg.filter.use_linear_filter);
|
ReadParam(section, "use_linear_filter", &cfg.filter.use_linear_filter);
|
||||||
|
ReadParam(section, "export_linear_aec_output",
|
||||||
|
&cfg.filter.export_linear_aec_output);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rtc::GetValueFromJsonObject(aec3_root, "erle", §ion)) {
|
if (rtc::GetValueFromJsonObject(aec3_root, "erle", §ion)) {
|
||||||
|
@ -314,10 +316,6 @@ void Aec3ConfigFromJsonString(absl::string_view json_string,
|
||||||
|
|
||||||
ReadParam(section, "floor_first_increase",
|
ReadParam(section, "floor_first_increase",
|
||||||
&cfg.suppressor.floor_first_increase);
|
&cfg.suppressor.floor_first_increase);
|
||||||
ReadParam(section, "enforce_transparent",
|
|
||||||
&cfg.suppressor.enforce_transparent);
|
|
||||||
ReadParam(section, "enforce_empty_higher_bands",
|
|
||||||
&cfg.suppressor.enforce_empty_higher_bands);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,7 +406,12 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) {
|
||||||
ost << "\"conservative_initial_phase\": "
|
ost << "\"conservative_initial_phase\": "
|
||||||
<< (config.filter.conservative_initial_phase ? "true" : "false") << ",";
|
<< (config.filter.conservative_initial_phase ? "true" : "false") << ",";
|
||||||
ost << "\"enable_shadow_filter_output_usage\": "
|
ost << "\"enable_shadow_filter_output_usage\": "
|
||||||
<< (config.filter.enable_shadow_filter_output_usage ? "true" : "false");
|
<< (config.filter.enable_shadow_filter_output_usage ? "true" : "false")
|
||||||
|
<< ",";
|
||||||
|
ost << "\"use_linear_filter\": "
|
||||||
|
<< (config.filter.use_linear_filter ? "true" : "false") << ",";
|
||||||
|
ost << "\"export_linear_aec_output\": "
|
||||||
|
<< (config.filter.export_linear_aec_output ? "true" : "false");
|
||||||
|
|
||||||
ost << "},";
|
ost << "},";
|
||||||
|
|
||||||
|
@ -545,12 +548,7 @@ std::string Aec3ConfigToJsonString(const EchoCanceller3Config& config) {
|
||||||
ost << "\"max_gain_during_echo\": "
|
ost << "\"max_gain_during_echo\": "
|
||||||
<< config.suppressor.high_bands_suppression.max_gain_during_echo;
|
<< config.suppressor.high_bands_suppression.max_gain_during_echo;
|
||||||
ost << "},";
|
ost << "},";
|
||||||
ost << "\"floor_first_increase\": " << config.suppressor.floor_first_increase
|
ost << "\"floor_first_increase\": " << config.suppressor.floor_first_increase;
|
||||||
<< ",";
|
|
||||||
ost << "\"enforce_transparent\": "
|
|
||||||
<< (config.suppressor.enforce_transparent ? "true" : "false") << ",";
|
|
||||||
ost << "\"enforce_empty_higher_bands\": "
|
|
||||||
<< (config.suppressor.enforce_empty_higher_bands ? "true" : "false");
|
|
||||||
ost << "}";
|
ost << "}";
|
||||||
ost << "}";
|
ost << "}";
|
||||||
ost << "}";
|
ost << "}";
|
||||||
|
|
|
@ -31,6 +31,12 @@ class EchoControl {
|
||||||
// Processes the capture signal in order to remove the echo.
|
// Processes the capture signal in order to remove the echo.
|
||||||
virtual void ProcessCapture(AudioBuffer* capture, bool echo_path_change) = 0;
|
virtual void ProcessCapture(AudioBuffer* capture, bool echo_path_change) = 0;
|
||||||
|
|
||||||
|
// As above, but also returns the linear filter output.
|
||||||
|
// TODO(peah): Make pure virtual.
|
||||||
|
virtual void ProcessCapture(AudioBuffer* capture,
|
||||||
|
AudioBuffer* linear_output,
|
||||||
|
bool level_change) {}
|
||||||
|
|
||||||
struct Metrics {
|
struct Metrics {
|
||||||
double echo_return_loss;
|
double echo_return_loss;
|
||||||
double echo_return_loss_enhancement;
|
double echo_return_loss_enhancement;
|
||||||
|
|
|
@ -42,7 +42,8 @@ constexpr int kMaxAdaptiveFilterLength = 50;
|
||||||
constexpr int kRenderTransferQueueSizeFrames = 100;
|
constexpr int kRenderTransferQueueSizeFrames = 100;
|
||||||
|
|
||||||
constexpr size_t kMaxNumBands = 3;
|
constexpr size_t kMaxNumBands = 3;
|
||||||
constexpr size_t kSubFrameLength = 80;
|
constexpr size_t kFrameSize = 160;
|
||||||
|
constexpr size_t kSubFrameLength = kFrameSize / 2;
|
||||||
|
|
||||||
constexpr size_t kBlockSize = kFftLengthBy2;
|
constexpr size_t kBlockSize = kFftLengthBy2;
|
||||||
constexpr size_t kBlockSizeLog2 = kFftLengthBy2Log2;
|
constexpr size_t kBlockSizeLog2 = kFftLengthBy2Log2;
|
||||||
|
|
|
@ -52,6 +52,7 @@ class BlockProcessorImpl final : public BlockProcessor {
|
||||||
void ProcessCapture(
|
void ProcessCapture(
|
||||||
bool echo_path_gain_change,
|
bool echo_path_gain_change,
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture_block) override;
|
std::vector<std::vector<std::vector<float>>>* capture_block) override;
|
||||||
|
|
||||||
void BufferRender(
|
void BufferRender(
|
||||||
|
@ -105,6 +106,7 @@ BlockProcessorImpl::~BlockProcessorImpl() = default;
|
||||||
void BlockProcessorImpl::ProcessCapture(
|
void BlockProcessorImpl::ProcessCapture(
|
||||||
bool echo_path_gain_change,
|
bool echo_path_gain_change,
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture_block) {
|
std::vector<std::vector<std::vector<float>>>* capture_block) {
|
||||||
RTC_DCHECK(capture_block);
|
RTC_DCHECK(capture_block);
|
||||||
RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->size());
|
RTC_DCHECK_EQ(NumBandsForRate(sample_rate_hz_), capture_block->size());
|
||||||
|
@ -191,7 +193,7 @@ void BlockProcessorImpl::ProcessCapture(
|
||||||
if (has_delay_estimator || render_buffer_->HasReceivedBufferDelay()) {
|
if (has_delay_estimator || render_buffer_->HasReceivedBufferDelay()) {
|
||||||
echo_remover_->ProcessCapture(
|
echo_remover_->ProcessCapture(
|
||||||
echo_path_variability, capture_signal_saturation, estimated_delay_,
|
echo_path_variability, capture_signal_saturation, estimated_delay_,
|
||||||
render_buffer_->GetRenderBuffer(), capture_block);
|
render_buffer_->GetRenderBuffer(), linear_output, capture_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the metrics.
|
// Update the metrics.
|
||||||
|
|
|
@ -59,6 +59,7 @@ class BlockProcessor {
|
||||||
virtual void ProcessCapture(
|
virtual void ProcessCapture(
|
||||||
bool echo_path_gain_change,
|
bool echo_path_gain_change,
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture_block) = 0;
|
std::vector<std::vector<std::vector<float>>>* capture_block) = 0;
|
||||||
|
|
||||||
// Buffers a block of render data supplied by a FrameBlocker object.
|
// Buffers a block of render data supplied by a FrameBlocker object.
|
||||||
|
|
|
@ -48,7 +48,7 @@ void RunBasicSetupAndApiCallTest(int sample_rate_hz, int num_iterations) {
|
||||||
std::vector<float>(kBlockSize, 1000.f)));
|
std::vector<float>(kBlockSize, 1000.f)));
|
||||||
for (int k = 0; k < num_iterations; ++k) {
|
for (int k = 0; k < num_iterations; ++k) {
|
||||||
block_processor->BufferRender(block);
|
block_processor->BufferRender(block);
|
||||||
block_processor->ProcessCapture(false, false, &block);
|
block_processor->ProcessCapture(false, false, nullptr, &block);
|
||||||
block_processor->UpdateEchoLeakageStatus(false);
|
block_processor->UpdateEchoLeakageStatus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,8 @@ void RunCaptureBlockSizeVerificationTest(int sample_rate_hz) {
|
||||||
std::vector<std::vector<float>>(kNumRenderChannels,
|
std::vector<std::vector<float>>(kNumRenderChannels,
|
||||||
std::vector<float>(kBlockSize - 1, 0.f)));
|
std::vector<float>(kBlockSize - 1, 0.f)));
|
||||||
|
|
||||||
EXPECT_DEATH(block_processor->ProcessCapture(false, false, &block), "");
|
EXPECT_DEATH(block_processor->ProcessCapture(false, false, nullptr, &block),
|
||||||
|
"");
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunRenderNumBandsVerificationTest(int sample_rate_hz) {
|
void RunRenderNumBandsVerificationTest(int sample_rate_hz) {
|
||||||
|
@ -117,7 +118,8 @@ void RunCaptureNumBandsVerificationTest(int sample_rate_hz) {
|
||||||
std::vector<std::vector<float>>(kNumRenderChannels,
|
std::vector<std::vector<float>>(kNumRenderChannels,
|
||||||
std::vector<float>(kBlockSize, 0.f)));
|
std::vector<float>(kBlockSize, 0.f)));
|
||||||
|
|
||||||
EXPECT_DEATH(block_processor->ProcessCapture(false, false, &block), "");
|
EXPECT_DEATH(block_processor->ProcessCapture(false, false, nullptr, &block),
|
||||||
|
"");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -172,7 +174,7 @@ TEST(BlockProcessor, DISABLED_DelayControllerIntegration) {
|
||||||
RandomizeSampleVector(&random_generator, render_block[0][0]);
|
RandomizeSampleVector(&random_generator, render_block[0][0]);
|
||||||
signal_delay_buffer.Delay(render_block[0][0], capture_block[0][0]);
|
signal_delay_buffer.Delay(render_block[0][0], capture_block[0][0]);
|
||||||
block_processor->BufferRender(render_block);
|
block_processor->BufferRender(render_block);
|
||||||
block_processor->ProcessCapture(false, false, &capture_block);
|
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,7 +209,7 @@ TEST(BlockProcessor, DISABLED_SubmoduleIntegration) {
|
||||||
.WillRepeatedly(Return(0));
|
.WillRepeatedly(Return(0));
|
||||||
EXPECT_CALL(*render_delay_controller_mock, GetDelay(_, _, _))
|
EXPECT_CALL(*render_delay_controller_mock, GetDelay(_, _, _))
|
||||||
.Times(kNumBlocks);
|
.Times(kNumBlocks);
|
||||||
EXPECT_CALL(*echo_remover_mock, ProcessCapture(_, _, _, _, _))
|
EXPECT_CALL(*echo_remover_mock, ProcessCapture(_, _, _, _, _, _))
|
||||||
.Times(kNumBlocks);
|
.Times(kNumBlocks);
|
||||||
EXPECT_CALL(*echo_remover_mock, UpdateEchoLeakageStatus(_))
|
EXPECT_CALL(*echo_remover_mock, UpdateEchoLeakageStatus(_))
|
||||||
.Times(kNumBlocks);
|
.Times(kNumBlocks);
|
||||||
|
@ -230,7 +232,7 @@ TEST(BlockProcessor, DISABLED_SubmoduleIntegration) {
|
||||||
RandomizeSampleVector(&random_generator, render_block[0][0]);
|
RandomizeSampleVector(&random_generator, render_block[0][0]);
|
||||||
signal_delay_buffer.Delay(render_block[0][0], capture_block[0][0]);
|
signal_delay_buffer.Delay(render_block[0][0], capture_block[0][0]);
|
||||||
block_processor->BufferRender(render_block);
|
block_processor->BufferRender(render_block);
|
||||||
block_processor->ProcessCapture(false, false, &capture_block);
|
block_processor->ProcessCapture(false, false, nullptr, &capture_block);
|
||||||
block_processor->UpdateEchoLeakageStatus(false);
|
block_processor->UpdateEchoLeakageStatus(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -284,7 +286,7 @@ TEST(BlockProcessor, VerifyCaptureNumBandsCheck) {
|
||||||
TEST(BlockProcessor, NullProcessCaptureParameter) {
|
TEST(BlockProcessor, NullProcessCaptureParameter) {
|
||||||
EXPECT_DEATH(std::unique_ptr<BlockProcessor>(
|
EXPECT_DEATH(std::unique_ptr<BlockProcessor>(
|
||||||
BlockProcessor::Create(EchoCanceller3Config(), 16000, 1, 1))
|
BlockProcessor::Create(EchoCanceller3Config(), 16000, 1, 1))
|
||||||
->ProcessCapture(false, false, nullptr),
|
->ProcessCapture(false, false, nullptr, nullptr),
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include "modules/audio_processing/high_pass_filter.h"
|
#include "modules/audio_processing/high_pass_filter.h"
|
||||||
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
#include "modules/audio_processing/logging/apm_data_dumper.h"
|
||||||
#include "rtc_base/atomic_ops.h"
|
#include "rtc_base/atomic_ops.h"
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
#include "system_wrappers/include/field_trial.h"
|
#include "system_wrappers/include/field_trial.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
@ -87,28 +88,52 @@ void FillSubFrameView(
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessCaptureFrameContent(
|
void ProcessCaptureFrameContent(
|
||||||
|
AudioBuffer* linear_output,
|
||||||
AudioBuffer* capture,
|
AudioBuffer* capture,
|
||||||
bool level_change,
|
bool level_change,
|
||||||
bool saturated_microphone_signal,
|
bool saturated_microphone_signal,
|
||||||
size_t sub_frame_index,
|
size_t sub_frame_index,
|
||||||
FrameBlocker* capture_blocker,
|
FrameBlocker* capture_blocker,
|
||||||
|
BlockFramer* linear_output_framer,
|
||||||
BlockFramer* output_framer,
|
BlockFramer* output_framer,
|
||||||
BlockProcessor* block_processor,
|
BlockProcessor* block_processor,
|
||||||
std::vector<std::vector<std::vector<float>>>* block,
|
std::vector<std::vector<std::vector<float>>>* linear_output_block,
|
||||||
std::vector<std::vector<rtc::ArrayView<float>>>* sub_frame_view) {
|
std::vector<std::vector<rtc::ArrayView<float>>>*
|
||||||
FillSubFrameView(capture, sub_frame_index, sub_frame_view);
|
linear_output_sub_frame_view,
|
||||||
capture_blocker->InsertSubFrameAndExtractBlock(*sub_frame_view, block);
|
std::vector<std::vector<std::vector<float>>>* capture_block,
|
||||||
|
std::vector<std::vector<rtc::ArrayView<float>>>* capture_sub_frame_view) {
|
||||||
|
FillSubFrameView(capture, sub_frame_index, capture_sub_frame_view);
|
||||||
|
|
||||||
|
if (linear_output) {
|
||||||
|
RTC_DCHECK(linear_output_framer);
|
||||||
|
RTC_DCHECK(linear_output_block);
|
||||||
|
RTC_DCHECK(linear_output_sub_frame_view);
|
||||||
|
FillSubFrameView(linear_output, sub_frame_index,
|
||||||
|
linear_output_sub_frame_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
capture_blocker->InsertSubFrameAndExtractBlock(*capture_sub_frame_view,
|
||||||
|
capture_block);
|
||||||
block_processor->ProcessCapture(level_change, saturated_microphone_signal,
|
block_processor->ProcessCapture(level_change, saturated_microphone_signal,
|
||||||
block);
|
linear_output_block, capture_block);
|
||||||
output_framer->InsertBlockAndExtractSubFrame(*block, sub_frame_view);
|
output_framer->InsertBlockAndExtractSubFrame(*capture_block,
|
||||||
|
capture_sub_frame_view);
|
||||||
|
|
||||||
|
if (linear_output) {
|
||||||
|
RTC_DCHECK(linear_output_framer);
|
||||||
|
linear_output_framer->InsertBlockAndExtractSubFrame(
|
||||||
|
*linear_output_block, linear_output_sub_frame_view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessRemainingCaptureFrameContent(
|
void ProcessRemainingCaptureFrameContent(
|
||||||
bool level_change,
|
bool level_change,
|
||||||
bool saturated_microphone_signal,
|
bool saturated_microphone_signal,
|
||||||
FrameBlocker* capture_blocker,
|
FrameBlocker* capture_blocker,
|
||||||
|
BlockFramer* linear_output_framer,
|
||||||
BlockFramer* output_framer,
|
BlockFramer* output_framer,
|
||||||
BlockProcessor* block_processor,
|
BlockProcessor* block_processor,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output_block,
|
||||||
std::vector<std::vector<std::vector<float>>>* block) {
|
std::vector<std::vector<std::vector<float>>>* block) {
|
||||||
if (!capture_blocker->IsBlockAvailable()) {
|
if (!capture_blocker->IsBlockAvailable()) {
|
||||||
return;
|
return;
|
||||||
|
@ -116,8 +141,13 @@ void ProcessRemainingCaptureFrameContent(
|
||||||
|
|
||||||
capture_blocker->ExtractBlock(block);
|
capture_blocker->ExtractBlock(block);
|
||||||
block_processor->ProcessCapture(level_change, saturated_microphone_signal,
|
block_processor->ProcessCapture(level_change, saturated_microphone_signal,
|
||||||
block);
|
linear_output_block, block);
|
||||||
output_framer->InsertBlock(*block);
|
output_framer->InsertBlock(*block);
|
||||||
|
|
||||||
|
if (linear_output_framer) {
|
||||||
|
RTC_DCHECK(linear_output_block);
|
||||||
|
linear_output_framer->InsertBlock(*linear_output_block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferRenderFrameContent(
|
void BufferRenderFrameContent(
|
||||||
|
@ -295,12 +325,24 @@ EchoCanceller3::EchoCanceller3(const EchoCanceller3Config& config,
|
||||||
|
|
||||||
RTC_DCHECK_EQ(num_bands_, std::max(sample_rate_hz_, 16000) / 16000);
|
RTC_DCHECK_EQ(num_bands_, std::max(sample_rate_hz_, 16000) / 16000);
|
||||||
RTC_DCHECK_GE(kMaxNumBands, num_bands_);
|
RTC_DCHECK_GE(kMaxNumBands, num_bands_);
|
||||||
|
|
||||||
|
if (config_.filter.export_linear_aec_output) {
|
||||||
|
linear_output_framer_.reset(new BlockFramer(1, num_capture_channels_));
|
||||||
|
linear_output_block_ =
|
||||||
|
std::make_unique<std::vector<std::vector<std::vector<float>>>>(
|
||||||
|
1, std::vector<std::vector<float>>(
|
||||||
|
num_capture_channels_, std::vector<float>(kBlockSize, 0.f)));
|
||||||
|
linear_output_sub_frame_view_ =
|
||||||
|
std::vector<std::vector<rtc::ArrayView<float>>>(
|
||||||
|
1, std::vector<rtc::ArrayView<float>>(num_capture_channels_));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EchoCanceller3::~EchoCanceller3() = default;
|
EchoCanceller3::~EchoCanceller3() = default;
|
||||||
|
|
||||||
void EchoCanceller3::AnalyzeRender(const AudioBuffer& render) {
|
void EchoCanceller3::AnalyzeRender(const AudioBuffer& render) {
|
||||||
RTC_DCHECK_RUNS_SERIALIZED(&render_race_checker_);
|
RTC_DCHECK_RUNS_SERIALIZED(&render_race_checker_);
|
||||||
|
|
||||||
RTC_DCHECK_EQ(render.num_channels(), num_render_channels_);
|
RTC_DCHECK_EQ(render.num_channels(), num_render_channels_);
|
||||||
data_dumper_->DumpRaw("aec3_call_order",
|
data_dumper_->DumpRaw("aec3_call_order",
|
||||||
static_cast<int>(EchoCanceller3ApiCall::kRender));
|
static_cast<int>(EchoCanceller3ApiCall::kRender));
|
||||||
|
@ -312,7 +354,6 @@ void EchoCanceller3::AnalyzeCapture(const AudioBuffer& capture) {
|
||||||
RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_);
|
RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_);
|
||||||
data_dumper_->DumpWav("aec3_capture_analyze_input", capture.num_frames(),
|
data_dumper_->DumpWav("aec3_capture_analyze_input", capture.num_frames(),
|
||||||
capture.channels_const()[0], sample_rate_hz_, 1);
|
capture.channels_const()[0], sample_rate_hz_, 1);
|
||||||
|
|
||||||
saturated_microphone_signal_ = false;
|
saturated_microphone_signal_ = false;
|
||||||
for (size_t channel = 0; channel < capture.num_channels(); ++channel) {
|
for (size_t channel = 0; channel < capture.num_channels(); ++channel) {
|
||||||
saturated_microphone_signal_ |=
|
saturated_microphone_signal_ |=
|
||||||
|
@ -325,6 +366,12 @@ void EchoCanceller3::AnalyzeCapture(const AudioBuffer& capture) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EchoCanceller3::ProcessCapture(AudioBuffer* capture, bool level_change) {
|
void EchoCanceller3::ProcessCapture(AudioBuffer* capture, bool level_change) {
|
||||||
|
ProcessCapture(capture, nullptr, level_change);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EchoCanceller3::ProcessCapture(AudioBuffer* capture,
|
||||||
|
AudioBuffer* linear_output,
|
||||||
|
bool level_change) {
|
||||||
RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_);
|
RTC_DCHECK_RUNS_SERIALIZED(&capture_race_checker_);
|
||||||
RTC_DCHECK(capture);
|
RTC_DCHECK(capture);
|
||||||
RTC_DCHECK_EQ(num_bands_, capture->num_bands());
|
RTC_DCHECK_EQ(num_bands_, capture->num_bands());
|
||||||
|
@ -333,6 +380,12 @@ void EchoCanceller3::ProcessCapture(AudioBuffer* capture, bool level_change) {
|
||||||
data_dumper_->DumpRaw("aec3_call_order",
|
data_dumper_->DumpRaw("aec3_call_order",
|
||||||
static_cast<int>(EchoCanceller3ApiCall::kCapture));
|
static_cast<int>(EchoCanceller3ApiCall::kCapture));
|
||||||
|
|
||||||
|
if (linear_output && !linear_output_framer_) {
|
||||||
|
RTC_LOG(LS_ERROR) << "Trying to retrieve the linear AEC output without "
|
||||||
|
"properly configuring AEC3.";
|
||||||
|
RTC_NOTREACHED();
|
||||||
|
}
|
||||||
|
|
||||||
// Report capture call in the metrics and periodically update API call
|
// Report capture call in the metrics and periodically update API call
|
||||||
// metrics.
|
// metrics.
|
||||||
api_call_metrics_.ReportCaptureCall();
|
api_call_metrics_.ReportCaptureCall();
|
||||||
|
@ -349,19 +402,24 @@ void EchoCanceller3::ProcessCapture(AudioBuffer* capture, bool level_change) {
|
||||||
|
|
||||||
EmptyRenderQueue();
|
EmptyRenderQueue();
|
||||||
|
|
||||||
ProcessCaptureFrameContent(capture, level_change,
|
ProcessCaptureFrameContent(linear_output, capture, level_change,
|
||||||
saturated_microphone_signal_, 0, &capture_blocker_,
|
saturated_microphone_signal_, 0, &capture_blocker_,
|
||||||
&output_framer_, block_processor_.get(),
|
linear_output_framer_.get(), &output_framer_,
|
||||||
&capture_block_, &capture_sub_frame_view_);
|
block_processor_.get(), linear_output_block_.get(),
|
||||||
|
&linear_output_sub_frame_view_, &capture_block_,
|
||||||
|
&capture_sub_frame_view_);
|
||||||
|
|
||||||
ProcessCaptureFrameContent(capture, level_change,
|
ProcessCaptureFrameContent(linear_output, capture, level_change,
|
||||||
saturated_microphone_signal_, 1, &capture_blocker_,
|
saturated_microphone_signal_, 1, &capture_blocker_,
|
||||||
&output_framer_, block_processor_.get(),
|
linear_output_framer_.get(), &output_framer_,
|
||||||
&capture_block_, &capture_sub_frame_view_);
|
block_processor_.get(), linear_output_block_.get(),
|
||||||
|
&linear_output_sub_frame_view_, &capture_block_,
|
||||||
|
&capture_sub_frame_view_);
|
||||||
|
|
||||||
ProcessRemainingCaptureFrameContent(
|
ProcessRemainingCaptureFrameContent(
|
||||||
level_change, saturated_microphone_signal_, &capture_blocker_,
|
level_change, saturated_microphone_signal_, &capture_blocker_,
|
||||||
&output_framer_, block_processor_.get(), &capture_block_);
|
linear_output_framer_.get(), &output_framer_, block_processor_.get(),
|
||||||
|
linear_output_block_.get(), &capture_block_);
|
||||||
|
|
||||||
data_dumper_->DumpWav("aec3_capture_output", AudioBuffer::kSplitBandSize,
|
data_dumper_->DumpWav("aec3_capture_output", AudioBuffer::kSplitBandSize,
|
||||||
&capture->split_bands(0)[0][0], 16000, 1);
|
&capture->split_bands(0)[0][0], 16000, 1);
|
||||||
|
|
|
@ -70,8 +70,6 @@ class Aec3RenderQueueItemVerifier {
|
||||||
// Main class for the echo canceller3.
|
// Main class for the echo canceller3.
|
||||||
// It does 4 things:
|
// It does 4 things:
|
||||||
// -Receives 10 ms frames of band-split audio.
|
// -Receives 10 ms frames of band-split audio.
|
||||||
// -Optionally applies an anti-hum (high-pass) filter on the
|
|
||||||
// received signals.
|
|
||||||
// -Provides the lower level echo canceller functionality with
|
// -Provides the lower level echo canceller functionality with
|
||||||
// blocks of 64 samples of audio data.
|
// blocks of 64 samples of audio data.
|
||||||
// -Partially handles the jitter in the render and capture API
|
// -Partially handles the jitter in the render and capture API
|
||||||
|
@ -106,6 +104,10 @@ class EchoCanceller3 : public EchoControl {
|
||||||
// Processes the split-band domain capture signal in order to remove any echo
|
// Processes the split-band domain capture signal in order to remove any echo
|
||||||
// present in the signal.
|
// present in the signal.
|
||||||
void ProcessCapture(AudioBuffer* capture, bool level_change) override;
|
void ProcessCapture(AudioBuffer* capture, bool level_change) override;
|
||||||
|
// As above, but also returns the linear filter output.
|
||||||
|
void ProcessCapture(AudioBuffer* capture,
|
||||||
|
AudioBuffer* linear_output,
|
||||||
|
bool level_change) override;
|
||||||
// Collect current metrics from the echo canceller.
|
// Collect current metrics from the echo canceller.
|
||||||
Metrics GetMetrics() const override;
|
Metrics GetMetrics() const override;
|
||||||
// Provides an optional external estimate of the audio buffer delay.
|
// Provides an optional external estimate of the audio buffer delay.
|
||||||
|
@ -149,6 +151,8 @@ class EchoCanceller3 : public EchoControl {
|
||||||
const int num_bands_;
|
const int num_bands_;
|
||||||
const size_t num_render_channels_;
|
const size_t num_render_channels_;
|
||||||
const size_t num_capture_channels_;
|
const size_t num_capture_channels_;
|
||||||
|
std::unique_ptr<BlockFramer> linear_output_framer_
|
||||||
|
RTC_GUARDED_BY(capture_race_checker_);
|
||||||
BlockFramer output_framer_ RTC_GUARDED_BY(capture_race_checker_);
|
BlockFramer output_framer_ RTC_GUARDED_BY(capture_race_checker_);
|
||||||
FrameBlocker capture_blocker_ RTC_GUARDED_BY(capture_race_checker_);
|
FrameBlocker capture_blocker_ RTC_GUARDED_BY(capture_race_checker_);
|
||||||
FrameBlocker render_blocker_ RTC_GUARDED_BY(capture_race_checker_);
|
FrameBlocker render_blocker_ RTC_GUARDED_BY(capture_race_checker_);
|
||||||
|
@ -163,10 +167,14 @@ class EchoCanceller3 : public EchoControl {
|
||||||
false;
|
false;
|
||||||
std::vector<std::vector<std::vector<float>>> render_block_
|
std::vector<std::vector<std::vector<float>>> render_block_
|
||||||
RTC_GUARDED_BY(capture_race_checker_);
|
RTC_GUARDED_BY(capture_race_checker_);
|
||||||
|
std::unique_ptr<std::vector<std::vector<std::vector<float>>>>
|
||||||
|
linear_output_block_ RTC_GUARDED_BY(capture_race_checker_);
|
||||||
std::vector<std::vector<std::vector<float>>> capture_block_
|
std::vector<std::vector<std::vector<float>>> capture_block_
|
||||||
RTC_GUARDED_BY(capture_race_checker_);
|
RTC_GUARDED_BY(capture_race_checker_);
|
||||||
std::vector<std::vector<rtc::ArrayView<float>>> render_sub_frame_view_
|
std::vector<std::vector<rtc::ArrayView<float>>> render_sub_frame_view_
|
||||||
RTC_GUARDED_BY(capture_race_checker_);
|
RTC_GUARDED_BY(capture_race_checker_);
|
||||||
|
std::vector<std::vector<rtc::ArrayView<float>>> linear_output_sub_frame_view_
|
||||||
|
RTC_GUARDED_BY(capture_race_checker_);
|
||||||
std::vector<std::vector<rtc::ArrayView<float>>> capture_sub_frame_view_
|
std::vector<std::vector<rtc::ArrayView<float>>> capture_sub_frame_view_
|
||||||
RTC_GUARDED_BY(capture_race_checker_);
|
RTC_GUARDED_BY(capture_race_checker_);
|
||||||
BlockDelayBuffer block_delay_buffer_ RTC_GUARDED_BY(capture_race_checker_);
|
BlockDelayBuffer block_delay_buffer_ RTC_GUARDED_BY(capture_race_checker_);
|
||||||
|
|
|
@ -112,6 +112,7 @@ class CaptureTransportVerificationProcessor : public BlockProcessor {
|
||||||
void ProcessCapture(
|
void ProcessCapture(
|
||||||
bool level_change,
|
bool level_change,
|
||||||
bool saturated_microphone_signal,
|
bool saturated_microphone_signal,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture_block) override {}
|
std::vector<std::vector<std::vector<float>>>* capture_block) override {}
|
||||||
|
|
||||||
void BufferRender(
|
void BufferRender(
|
||||||
|
@ -137,6 +138,7 @@ class RenderTransportVerificationProcessor : public BlockProcessor {
|
||||||
void ProcessCapture(
|
void ProcessCapture(
|
||||||
bool level_change,
|
bool level_change,
|
||||||
bool saturated_microphone_signal,
|
bool saturated_microphone_signal,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture_block) override {
|
std::vector<std::vector<std::vector<float>>>* capture_block) override {
|
||||||
std::vector<std::vector<std::vector<float>>> render_block =
|
std::vector<std::vector<std::vector<float>>> render_block =
|
||||||
received_render_blocks_.front();
|
received_render_blocks_.front();
|
||||||
|
@ -267,17 +269,17 @@ class EchoCanceller3Tester {
|
||||||
|
|
||||||
switch (echo_path_change_test_variant) {
|
switch (echo_path_change_test_variant) {
|
||||||
case EchoPathChangeTestVariant::kNone:
|
case EchoPathChangeTestVariant::kNone:
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _, _))
|
||||||
.Times(kExpectedNumBlocksToProcess);
|
.Times(kExpectedNumBlocksToProcess);
|
||||||
break;
|
break;
|
||||||
case EchoPathChangeTestVariant::kOneSticky:
|
case EchoPathChangeTestVariant::kOneSticky:
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _, _))
|
||||||
.Times(kExpectedNumBlocksToProcess);
|
.Times(kExpectedNumBlocksToProcess);
|
||||||
break;
|
break;
|
||||||
case EchoPathChangeTestVariant::kOneNonSticky:
|
case EchoPathChangeTestVariant::kOneNonSticky:
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(true, _, _, _))
|
||||||
.Times(kNumFullBlocksPerFrame);
|
.Times(kNumFullBlocksPerFrame);
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(false, _, _, _))
|
||||||
.Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
|
.Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -338,7 +340,7 @@ class EchoCanceller3Tester {
|
||||||
new StrictMock<webrtc::test::MockBlockProcessor>());
|
new StrictMock<webrtc::test::MockBlockProcessor>());
|
||||||
EXPECT_CALL(*block_processor_mock, BufferRender(_))
|
EXPECT_CALL(*block_processor_mock, BufferRender(_))
|
||||||
.Times(kExpectedNumBlocksToProcess);
|
.Times(kExpectedNumBlocksToProcess);
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, _, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, _, _, _))
|
||||||
.Times(kExpectedNumBlocksToProcess);
|
.Times(kExpectedNumBlocksToProcess);
|
||||||
|
|
||||||
switch (leakage_report_variant) {
|
switch (leakage_report_variant) {
|
||||||
|
@ -429,21 +431,21 @@ class EchoCanceller3Tester {
|
||||||
|
|
||||||
switch (saturation_variant) {
|
switch (saturation_variant) {
|
||||||
case SaturationTestVariant::kNone:
|
case SaturationTestVariant::kNone:
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _, _))
|
||||||
.Times(kExpectedNumBlocksToProcess);
|
.Times(kExpectedNumBlocksToProcess);
|
||||||
break;
|
break;
|
||||||
case SaturationTestVariant::kOneNegative: {
|
case SaturationTestVariant::kOneNegative: {
|
||||||
::testing::InSequence s;
|
::testing::InSequence s;
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _, _))
|
||||||
.Times(kNumFullBlocksPerFrame);
|
.Times(kNumFullBlocksPerFrame);
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _, _))
|
||||||
.Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
|
.Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
|
||||||
} break;
|
} break;
|
||||||
case SaturationTestVariant::kOnePositive: {
|
case SaturationTestVariant::kOnePositive: {
|
||||||
::testing::InSequence s;
|
::testing::InSequence s;
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, true, _, _))
|
||||||
.Times(kNumFullBlocksPerFrame);
|
.Times(kNumFullBlocksPerFrame);
|
||||||
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _))
|
EXPECT_CALL(*block_processor_mock, ProcessCapture(_, false, _, _))
|
||||||
.Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
|
.Times(kExpectedNumBlocksToProcess - kNumFullBlocksPerFrame);
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,7 @@ class EchoRemoverImpl final : public EchoRemover {
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
const absl::optional<DelayEstimate>& external_delay,
|
const absl::optional<DelayEstimate>& external_delay,
|
||||||
RenderBuffer* render_buffer,
|
RenderBuffer* render_buffer,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture) override;
|
std::vector<std::vector<std::vector<float>>>* capture) override;
|
||||||
|
|
||||||
// Updates the status on whether echo leakage is detected in the output of the
|
// Updates the status on whether echo leakage is detected in the output of the
|
||||||
|
@ -235,6 +236,7 @@ void EchoRemoverImpl::ProcessCapture(
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
const absl::optional<DelayEstimate>& external_delay,
|
const absl::optional<DelayEstimate>& external_delay,
|
||||||
RenderBuffer* render_buffer,
|
RenderBuffer* render_buffer,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture) {
|
std::vector<std::vector<std::vector<float>>>* capture) {
|
||||||
++block_counter_;
|
++block_counter_;
|
||||||
const std::vector<std::vector<std::vector<float>>>& x =
|
const std::vector<std::vector<std::vector<float>>>& x =
|
||||||
|
@ -367,6 +369,16 @@ void EchoRemoverImpl::ProcessCapture(
|
||||||
E[ch].Spectrum(optimization_, E2[ch]);
|
E[ch].Spectrum(optimization_, E2[ch]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optionally return the linear filter output.
|
||||||
|
if (linear_output) {
|
||||||
|
RTC_DCHECK_GE(1, linear_output->size());
|
||||||
|
RTC_DCHECK_EQ(num_capture_channels_, linear_output[0].size());
|
||||||
|
for (size_t ch = 0; ch < num_capture_channels_; ++ch) {
|
||||||
|
RTC_DCHECK_EQ(kBlockSize, (*linear_output)[0][ch].size());
|
||||||
|
std::copy(e[ch].begin(), e[ch].end(), (*linear_output)[0][ch].begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update the AEC state information.
|
// Update the AEC state information.
|
||||||
aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponses(),
|
aec_state_.Update(external_delay, subtractor_.FilterFrequencyResponses(),
|
||||||
subtractor_.FilterImpulseResponses(), *render_buffer, E2,
|
subtractor_.FilterImpulseResponses(), *render_buffer, E2,
|
||||||
|
|
|
@ -42,6 +42,7 @@ class EchoRemover {
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
const absl::optional<DelayEstimate>& external_delay,
|
const absl::optional<DelayEstimate>& external_delay,
|
||||||
RenderBuffer* render_buffer,
|
RenderBuffer* render_buffer,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture) = 0;
|
std::vector<std::vector<std::vector<float>>>* capture) = 0;
|
||||||
|
|
||||||
// Updates the status on whether echo leakage is detected in the output of the
|
// Updates the status on whether echo leakage is detected in the output of the
|
||||||
|
|
|
@ -73,9 +73,9 @@ TEST(EchoRemover, BasicApiCalls) {
|
||||||
render_buffer->Insert(render);
|
render_buffer->Insert(render);
|
||||||
render_buffer->PrepareCaptureProcessing();
|
render_buffer->PrepareCaptureProcessing();
|
||||||
|
|
||||||
remover->ProcessCapture(echo_path_variability,
|
remover->ProcessCapture(
|
||||||
k % 2 == 0 ? true : false, delay_estimate,
|
echo_path_variability, k % 2 == 0 ? true : false, delay_estimate,
|
||||||
render_buffer->GetRenderBuffer(), &capture);
|
render_buffer->GetRenderBuffer(), nullptr, &capture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,9 +107,9 @@ TEST(EchoRemover, WrongCaptureBlockSize) {
|
||||||
1, std::vector<float>(kBlockSize - 1, 0.f)));
|
1, std::vector<float>(kBlockSize - 1, 0.f)));
|
||||||
EchoPathVariability echo_path_variability(
|
EchoPathVariability echo_path_variability(
|
||||||
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
||||||
EXPECT_DEATH(
|
EXPECT_DEATH(remover->ProcessCapture(
|
||||||
remover->ProcessCapture(echo_path_variability, false, delay_estimate,
|
echo_path_variability, false, delay_estimate,
|
||||||
render_buffer->GetRenderBuffer(), &capture),
|
render_buffer->GetRenderBuffer(), nullptr, &capture),
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,9 +131,9 @@ TEST(EchoRemover, DISABLED_WrongCaptureNumBands) {
|
||||||
std::vector<float>(kBlockSize, 0.f)));
|
std::vector<float>(kBlockSize, 0.f)));
|
||||||
EchoPathVariability echo_path_variability(
|
EchoPathVariability echo_path_variability(
|
||||||
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
||||||
EXPECT_DEATH(
|
EXPECT_DEATH(remover->ProcessCapture(
|
||||||
remover->ProcessCapture(echo_path_variability, false, delay_estimate,
|
echo_path_variability, false, delay_estimate,
|
||||||
render_buffer->GetRenderBuffer(), &capture),
|
render_buffer->GetRenderBuffer(), nullptr, &capture),
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,9 +147,9 @@ TEST(EchoRemover, NullCapture) {
|
||||||
RenderDelayBuffer::Create(EchoCanceller3Config(), 16000, 1));
|
RenderDelayBuffer::Create(EchoCanceller3Config(), 16000, 1));
|
||||||
EchoPathVariability echo_path_variability(
|
EchoPathVariability echo_path_variability(
|
||||||
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
false, EchoPathVariability::DelayAdjustment::kNone, false);
|
||||||
EXPECT_DEATH(
|
EXPECT_DEATH(remover->ProcessCapture(
|
||||||
remover->ProcessCapture(echo_path_variability, false, delay_estimate,
|
echo_path_variability, false, delay_estimate,
|
||||||
render_buffer->GetRenderBuffer(), nullptr),
|
render_buffer->GetRenderBuffer(), nullptr, nullptr),
|
||||||
"");
|
"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +222,8 @@ TEST(EchoRemover, BasicEchoRemoval) {
|
||||||
render_buffer->PrepareCaptureProcessing();
|
render_buffer->PrepareCaptureProcessing();
|
||||||
|
|
||||||
remover->ProcessCapture(echo_path_variability, false, delay_estimate,
|
remover->ProcessCapture(echo_path_variability, false, delay_estimate,
|
||||||
render_buffer->GetRenderBuffer(), &y);
|
render_buffer->GetRenderBuffer(), nullptr,
|
||||||
|
&y);
|
||||||
|
|
||||||
if (k > kNumBlocksToProcess / 2) {
|
if (k > kNumBlocksToProcess / 2) {
|
||||||
output_energy = std::inner_product(y[0][0].begin(), y[0][0].end(),
|
output_energy = std::inner_product(y[0][0].begin(), y[0][0].end(),
|
||||||
|
|
|
@ -24,10 +24,11 @@ class MockBlockProcessor : public BlockProcessor {
|
||||||
MockBlockProcessor();
|
MockBlockProcessor();
|
||||||
virtual ~MockBlockProcessor();
|
virtual ~MockBlockProcessor();
|
||||||
|
|
||||||
MOCK_METHOD3(
|
MOCK_METHOD4(
|
||||||
ProcessCapture,
|
ProcessCapture,
|
||||||
void(bool level_change,
|
void(bool level_change,
|
||||||
bool saturated_microphone_signal,
|
bool saturated_microphone_signal,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture_block));
|
std::vector<std::vector<std::vector<float>>>* capture_block));
|
||||||
MOCK_METHOD1(BufferRender,
|
MOCK_METHOD1(BufferRender,
|
||||||
void(const std::vector<std::vector<std::vector<float>>>& block));
|
void(const std::vector<std::vector<std::vector<float>>>& block));
|
||||||
|
|
|
@ -27,11 +27,12 @@ class MockEchoRemover : public EchoRemover {
|
||||||
MockEchoRemover();
|
MockEchoRemover();
|
||||||
virtual ~MockEchoRemover();
|
virtual ~MockEchoRemover();
|
||||||
|
|
||||||
MOCK_METHOD5(ProcessCapture,
|
MOCK_METHOD6(ProcessCapture,
|
||||||
void(EchoPathVariability echo_path_variability,
|
void(EchoPathVariability echo_path_variability,
|
||||||
bool capture_signal_saturation,
|
bool capture_signal_saturation,
|
||||||
const absl::optional<DelayEstimate>& delay_estimate,
|
const absl::optional<DelayEstimate>& delay_estimate,
|
||||||
RenderBuffer* render_buffer,
|
RenderBuffer* render_buffer,
|
||||||
|
std::vector<std::vector<std::vector<float>>>* linear_output,
|
||||||
std::vector<std::vector<std::vector<float>>>* capture));
|
std::vector<std::vector<std::vector<float>>>* capture));
|
||||||
MOCK_CONST_METHOD0(Delay, absl::optional<int>());
|
MOCK_CONST_METHOD0(Delay, absl::optional<int>());
|
||||||
MOCK_METHOD1(UpdateEchoLeakageStatus, void(bool leakage_detected));
|
MOCK_METHOD1(UpdateEchoLeakageStatus, void(bool leakage_detected));
|
||||||
|
|
|
@ -343,13 +343,6 @@ void SuppressionGain::GetGain(
|
||||||
std::array<float, kFftLengthBy2Plus1>* low_band_gain) {
|
std::array<float, kFftLengthBy2Plus1>* low_band_gain) {
|
||||||
RTC_DCHECK(high_bands_gain);
|
RTC_DCHECK(high_bands_gain);
|
||||||
RTC_DCHECK(low_band_gain);
|
RTC_DCHECK(low_band_gain);
|
||||||
const auto& cfg = config_.suppressor;
|
|
||||||
|
|
||||||
if (cfg.enforce_transparent) {
|
|
||||||
low_band_gain->fill(1.f);
|
|
||||||
*high_bands_gain = cfg.enforce_empty_higher_bands ? 0.f : 1.f;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the nearend state selection.
|
// Update the nearend state selection.
|
||||||
dominant_nearend_detector_.Update(nearend_spectrum, residual_echo_spectrum,
|
dominant_nearend_detector_.Update(nearend_spectrum, residual_echo_spectrum,
|
||||||
|
@ -360,11 +353,6 @@ void SuppressionGain::GetGain(
|
||||||
LowerBandGain(low_noise_render, aec_state, nearend_spectrum,
|
LowerBandGain(low_noise_render, aec_state, nearend_spectrum,
|
||||||
residual_echo_spectrum, comfort_noise_spectrum, low_band_gain);
|
residual_echo_spectrum, comfort_noise_spectrum, low_band_gain);
|
||||||
|
|
||||||
if (cfg.enforce_empty_higher_bands) {
|
|
||||||
*high_bands_gain = 0.f;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compute the gain for the upper bands.
|
// Compute the gain for the upper bands.
|
||||||
const absl::optional<int> narrow_peak_band =
|
const absl::optional<int> narrow_peak_band =
|
||||||
render_signal_analyzer.NarrowPeakBand();
|
render_signal_analyzer.NarrowPeakBand();
|
||||||
|
|
|
@ -1342,8 +1342,9 @@ int AudioProcessingImpl::ProcessCaptureStreamLocked() {
|
||||||
submodules_.echo_controller->SetAudioBufferDelay(stream_delay_ms());
|
submodules_.echo_controller->SetAudioBufferDelay(stream_delay_ms());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioBuffer* linear_aec_buffer = capture_.linear_aec_output.get();
|
||||||
submodules_.echo_controller->ProcessCapture(
|
submodules_.echo_controller->ProcessCapture(
|
||||||
capture_buffer, capture_.echo_path_gain_change);
|
capture_buffer, linear_aec_buffer, capture_.echo_path_gain_change);
|
||||||
} else if (submodules_.echo_cancellation) {
|
} else if (submodules_.echo_cancellation) {
|
||||||
// Ensure that the stream delay was set before the call to the
|
// Ensure that the stream delay was set before the call to the
|
||||||
// AEC ProcessCaptureAudio function.
|
// AEC ProcessCaptureAudio function.
|
||||||
|
@ -1625,6 +1626,31 @@ int AudioProcessingImpl::set_stream_delay_ms(int delay) {
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AudioProcessingImpl::GetLinearAecOutput(
|
||||||
|
rtc::ArrayView<std::array<float, 160>> linear_output) const {
|
||||||
|
rtc::CritScope cs(&crit_capture_);
|
||||||
|
AudioBuffer* linear_aec_buffer = capture_.linear_aec_output.get();
|
||||||
|
|
||||||
|
RTC_DCHECK(linear_aec_buffer);
|
||||||
|
if (linear_aec_buffer) {
|
||||||
|
RTC_DCHECK_EQ(1, linear_aec_buffer->num_bands());
|
||||||
|
RTC_DCHECK_EQ(linear_output.size(), linear_aec_buffer->num_channels());
|
||||||
|
|
||||||
|
for (size_t ch = 0; ch < linear_aec_buffer->num_channels(); ++ch) {
|
||||||
|
RTC_DCHECK_EQ(linear_output[ch].size(), linear_aec_buffer->num_frames());
|
||||||
|
rtc::ArrayView<const float> channel_view =
|
||||||
|
rtc::ArrayView<const float>(linear_aec_buffer->channels_const()[ch],
|
||||||
|
linear_aec_buffer->num_frames());
|
||||||
|
std::copy(channel_view.begin(), channel_view.end(),
|
||||||
|
linear_output[ch].begin());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
RTC_LOG(LS_ERROR) << "No linear AEC output available";
|
||||||
|
RTC_NOTREACHED();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int AudioProcessingImpl::stream_delay_ms() const {
|
int AudioProcessingImpl::stream_delay_ms() const {
|
||||||
// Used as callback from submodules, hence locking is not allowed.
|
// Used as callback from submodules, hence locking is not allowed.
|
||||||
return capture_nonlocked_.stream_delay_ms;
|
return capture_nonlocked_.stream_delay_ms;
|
||||||
|
@ -1790,6 +1816,16 @@ void AudioProcessingImpl::InitializeEchoController() {
|
||||||
num_proc_channels());
|
num_proc_channels());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup the storage for returning the linear AEC output.
|
||||||
|
if (config_.echo_canceller.export_linear_aec_output) {
|
||||||
|
constexpr int kLinearOutputRateHz = 16000;
|
||||||
|
capture_.linear_aec_output = std::make_unique<AudioBuffer>(
|
||||||
|
kLinearOutputRateHz, num_proc_channels(), kLinearOutputRateHz,
|
||||||
|
num_proc_channels(), kLinearOutputRateHz, num_proc_channels());
|
||||||
|
} else {
|
||||||
|
capture_.linear_aec_output.reset();
|
||||||
|
}
|
||||||
|
|
||||||
capture_nonlocked_.echo_controller_enabled = true;
|
capture_nonlocked_.echo_controller_enabled = true;
|
||||||
|
|
||||||
submodules_.echo_cancellation.reset();
|
submodules_.echo_cancellation.reset();
|
||||||
|
@ -1801,6 +1837,7 @@ void AudioProcessingImpl::InitializeEchoController() {
|
||||||
|
|
||||||
submodules_.echo_controller.reset();
|
submodules_.echo_controller.reset();
|
||||||
capture_nonlocked_.echo_controller_enabled = false;
|
capture_nonlocked_.echo_controller_enabled = false;
|
||||||
|
capture_.linear_aec_output.reset();
|
||||||
|
|
||||||
if (!config_.echo_canceller.enabled) {
|
if (!config_.echo_canceller.enabled) {
|
||||||
submodules_.echo_cancellation.reset();
|
submodules_.echo_cancellation.reset();
|
||||||
|
|
|
@ -87,6 +87,8 @@ class AudioProcessingImpl : public AudioProcessing {
|
||||||
const StreamConfig& input_config,
|
const StreamConfig& input_config,
|
||||||
const StreamConfig& output_config,
|
const StreamConfig& output_config,
|
||||||
float* const* dest) override;
|
float* const* dest) override;
|
||||||
|
bool GetLinearAecOutput(
|
||||||
|
rtc::ArrayView<std::array<float, 160>> linear_output) const override;
|
||||||
void set_output_will_be_muted(bool muted) override;
|
void set_output_will_be_muted(bool muted) override;
|
||||||
int set_stream_delay_ms(int delay) override;
|
int set_stream_delay_ms(int delay) override;
|
||||||
void set_delay_offset_ms(int offset) override;
|
void set_delay_offset_ms(int offset) override;
|
||||||
|
@ -412,6 +414,7 @@ class AudioProcessingImpl : public AudioProcessing {
|
||||||
bool transient_suppressor_enabled;
|
bool transient_suppressor_enabled;
|
||||||
std::unique_ptr<AudioBuffer> capture_audio;
|
std::unique_ptr<AudioBuffer> capture_audio;
|
||||||
std::unique_ptr<AudioBuffer> capture_fullband_audio;
|
std::unique_ptr<AudioBuffer> capture_fullband_audio;
|
||||||
|
std::unique_ptr<AudioBuffer> linear_aec_output;
|
||||||
// Only the rate and samples fields of capture_processing_format_ are used
|
// Only the rate and samples fields of capture_processing_format_ are used
|
||||||
// because the capture processing number of channels is mutable and is
|
// because the capture processing number of channels is mutable and is
|
||||||
// tracked by the capture_audio_.
|
// tracked by the capture_audio_.
|
||||||
|
|
|
@ -242,13 +242,13 @@ TEST(AudioProcessingImplTest,
|
||||||
|
|
||||||
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
||||||
EXPECT_CALL(*echo_control_mock,
|
EXPECT_CALL(*echo_control_mock,
|
||||||
ProcessCapture(NotNull(), /*echo_path_change=*/false))
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
apm->ProcessStream(&frame);
|
apm->ProcessStream(&frame);
|
||||||
|
|
||||||
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
||||||
EXPECT_CALL(*echo_control_mock,
|
EXPECT_CALL(*echo_control_mock,
|
||||||
ProcessCapture(NotNull(), /*echo_path_change=*/true))
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
apm->SetRuntimeSetting(
|
apm->SetRuntimeSetting(
|
||||||
AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
|
AudioProcessing::RuntimeSetting::CreateCapturePreGain(2.f));
|
||||||
|
@ -286,7 +286,8 @@ TEST(AudioProcessingImplTest,
|
||||||
|
|
||||||
const int initial_analog_gain = apm->recommended_stream_analog_level();
|
const int initial_analog_gain = apm->recommended_stream_analog_level();
|
||||||
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
||||||
EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), false)).Times(1);
|
EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), testing::_, false))
|
||||||
|
.Times(1);
|
||||||
apm->ProcessStream(&frame);
|
apm->ProcessStream(&frame);
|
||||||
|
|
||||||
// Force an analog gain change if it did not happen.
|
// Force an analog gain change if it did not happen.
|
||||||
|
@ -295,7 +296,8 @@ TEST(AudioProcessingImplTest,
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
||||||
EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), true)).Times(1);
|
EXPECT_CALL(*echo_control_mock, ProcessCapture(NotNull(), testing::_, true))
|
||||||
|
.Times(1);
|
||||||
apm->ProcessStream(&frame);
|
apm->ProcessStream(&frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,13 +328,13 @@ TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) {
|
||||||
|
|
||||||
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
||||||
EXPECT_CALL(*echo_control_mock,
|
EXPECT_CALL(*echo_control_mock,
|
||||||
ProcessCapture(NotNull(), /*echo_path_change=*/false))
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
apm->ProcessStream(&frame);
|
apm->ProcessStream(&frame);
|
||||||
|
|
||||||
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
||||||
EXPECT_CALL(*echo_control_mock,
|
EXPECT_CALL(*echo_control_mock,
|
||||||
ProcessCapture(NotNull(), /*echo_path_change=*/false))
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
apm->SetRuntimeSetting(
|
apm->SetRuntimeSetting(
|
||||||
AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
|
AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
|
||||||
|
@ -340,7 +342,7 @@ TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) {
|
||||||
|
|
||||||
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
||||||
EXPECT_CALL(*echo_control_mock,
|
EXPECT_CALL(*echo_control_mock,
|
||||||
ProcessCapture(NotNull(), /*echo_path_change=*/false))
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/false))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
apm->SetRuntimeSetting(
|
apm->SetRuntimeSetting(
|
||||||
AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
|
AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(50));
|
||||||
|
@ -348,7 +350,7 @@ TEST(AudioProcessingImplTest, EchoControllerObservesPlayoutVolumeChange) {
|
||||||
|
|
||||||
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
EXPECT_CALL(*echo_control_mock, AnalyzeCapture(testing::_)).Times(1);
|
||||||
EXPECT_CALL(*echo_control_mock,
|
EXPECT_CALL(*echo_control_mock,
|
||||||
ProcessCapture(NotNull(), /*echo_path_change=*/true))
|
ProcessCapture(NotNull(), testing::_, /*echo_path_change=*/true))
|
||||||
.Times(1);
|
.Times(1);
|
||||||
apm->SetRuntimeSetting(
|
apm->SetRuntimeSetting(
|
||||||
AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100));
|
AudioProcessing::RuntimeSetting::CreatePlayoutVolumeChange(100));
|
||||||
|
|
|
@ -2425,7 +2425,8 @@ class MyEchoControlFactory : public EchoControlFactory {
|
||||||
auto ec = new test::MockEchoControl();
|
auto ec = new test::MockEchoControl();
|
||||||
EXPECT_CALL(*ec, AnalyzeRender(::testing::_)).Times(1);
|
EXPECT_CALL(*ec, AnalyzeRender(::testing::_)).Times(1);
|
||||||
EXPECT_CALL(*ec, AnalyzeCapture(::testing::_)).Times(2);
|
EXPECT_CALL(*ec, AnalyzeCapture(::testing::_)).Times(2);
|
||||||
EXPECT_CALL(*ec, ProcessCapture(::testing::_, ::testing::_)).Times(2);
|
EXPECT_CALL(*ec, ProcessCapture(::testing::_, ::testing::_, ::testing::_))
|
||||||
|
.Times(2);
|
||||||
return std::unique_ptr<EchoControl>(ec);
|
return std::unique_ptr<EchoControl>(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -280,6 +280,7 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface {
|
||||||
bool legacy_moderate_suppression_level = false;
|
bool legacy_moderate_suppression_level = false;
|
||||||
// Recommended not to use. Will be removed in the future.
|
// Recommended not to use. Will be removed in the future.
|
||||||
bool use_legacy_aec = false;
|
bool use_legacy_aec = false;
|
||||||
|
bool export_linear_aec_output = false;
|
||||||
} echo_canceller;
|
} echo_canceller;
|
||||||
|
|
||||||
// Enables background noise suppression.
|
// Enables background noise suppression.
|
||||||
|
@ -611,6 +612,13 @@ class RTC_EXPORT AudioProcessing : public rtc::RefCountInterface {
|
||||||
virtual int AnalyzeReverseStream(const float* const* data,
|
virtual int AnalyzeReverseStream(const float* const* data,
|
||||||
const StreamConfig& reverse_config) = 0;
|
const StreamConfig& reverse_config) = 0;
|
||||||
|
|
||||||
|
// Returns the most recently produced 10 ms of the linear AEC output at a rate
|
||||||
|
// of 16 kHz. If there is more than one capture channel, a mono representation
|
||||||
|
// of the input is returned. Returns true/false to indicate whether an output
|
||||||
|
// returned.
|
||||||
|
virtual bool GetLinearAecOutput(
|
||||||
|
rtc::ArrayView<std::array<float, 160>> linear_output) const = 0;
|
||||||
|
|
||||||
// This must be called prior to ProcessStream() if and only if adaptive analog
|
// This must be called prior to ProcessStream() if and only if adaptive analog
|
||||||
// gain control is enabled, to pass the current analog level from the audio
|
// gain control is enabled, to pass the current analog level from the audio
|
||||||
// HAL. Must be within the range provided in Config::GainController1.
|
// HAL. Must be within the range provided in Config::GainController1.
|
||||||
|
|
|
@ -47,6 +47,10 @@ class MockEchoControl : public EchoControl {
|
||||||
MOCK_METHOD1(AnalyzeCapture, void(AudioBuffer* capture));
|
MOCK_METHOD1(AnalyzeCapture, void(AudioBuffer* capture));
|
||||||
MOCK_METHOD2(ProcessCapture,
|
MOCK_METHOD2(ProcessCapture,
|
||||||
void(AudioBuffer* capture, bool echo_path_change));
|
void(AudioBuffer* capture, bool echo_path_change));
|
||||||
|
MOCK_METHOD3(ProcessCapture,
|
||||||
|
void(AudioBuffer* capture,
|
||||||
|
AudioBuffer* linear_output,
|
||||||
|
bool echo_path_change));
|
||||||
MOCK_CONST_METHOD0(GetMetrics, Metrics());
|
MOCK_CONST_METHOD0(GetMetrics, Metrics());
|
||||||
MOCK_METHOD1(SetAudioBufferDelay, void(int delay_ms));
|
MOCK_METHOD1(SetAudioBufferDelay, void(int delay_ms));
|
||||||
MOCK_CONST_METHOD0(ActiveProcessing, bool());
|
MOCK_CONST_METHOD0(ActiveProcessing, bool());
|
||||||
|
@ -105,6 +109,9 @@ class MockAudioProcessing : public ::testing::NiceMock<AudioProcessing> {
|
||||||
const StreamConfig& input_config,
|
const StreamConfig& input_config,
|
||||||
const StreamConfig& output_config,
|
const StreamConfig& output_config,
|
||||||
float* const* dest));
|
float* const* dest));
|
||||||
|
MOCK_CONST_METHOD1(
|
||||||
|
GetLinearAecOutput,
|
||||||
|
bool(rtc::ArrayView<std::array<float, 160>> linear_output));
|
||||||
MOCK_METHOD1(set_stream_delay_ms, int(int delay));
|
MOCK_METHOD1(set_stream_delay_ms, int(int delay));
|
||||||
MOCK_CONST_METHOD0(stream_delay_ms, int());
|
MOCK_CONST_METHOD0(stream_delay_ms, int());
|
||||||
MOCK_CONST_METHOD0(was_stream_delay_set, bool());
|
MOCK_CONST_METHOD0(was_stream_delay_set, bool());
|
||||||
|
|
|
@ -227,6 +227,20 @@ void AudioProcessingSimulator::ProcessStream(bool fixed_interface) {
|
||||||
buffer_file_writer_->Write(*out_buf_);
|
buffer_file_writer_->Write(*out_buf_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (linear_aec_output_file_writer_) {
|
||||||
|
bool output_available = ap_->GetLinearAecOutput(linear_aec_output_buf_);
|
||||||
|
RTC_CHECK(output_available);
|
||||||
|
RTC_CHECK_GT(linear_aec_output_buf_.size(), 0);
|
||||||
|
RTC_CHECK_EQ(linear_aec_output_buf_[0].size(), 160);
|
||||||
|
for (size_t k = 0; k < linear_aec_output_buf_[0].size(); ++k) {
|
||||||
|
for (size_t ch = 0; ch < linear_aec_output_buf_.size(); ++ch) {
|
||||||
|
RTC_CHECK_EQ(linear_aec_output_buf_[ch].size(), 160);
|
||||||
|
linear_aec_output_file_writer_->WriteSamples(
|
||||||
|
&linear_aec_output_buf_[ch][k], 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (residual_echo_likelihood_graph_writer_.is_open()) {
|
if (residual_echo_likelihood_graph_writer_.is_open()) {
|
||||||
auto stats = ap_->GetStatistics(true /*has_remote_tracks*/);
|
auto stats = ap_->GetStatistics(true /*has_remote_tracks*/);
|
||||||
residual_echo_likelihood_graph_writer_
|
residual_echo_likelihood_graph_writer_
|
||||||
|
@ -342,6 +356,21 @@ void AudioProcessingSimulator::SetupOutput() {
|
||||||
settings_.processed_capture_samples);
|
settings_.processed_capture_samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings_.linear_aec_output_filename) {
|
||||||
|
std::string filename;
|
||||||
|
if (settings_.store_intermediate_output) {
|
||||||
|
filename = GetIndexedOutputWavFilename(
|
||||||
|
*settings_.linear_aec_output_filename, output_reset_counter_);
|
||||||
|
} else {
|
||||||
|
filename = *settings_.linear_aec_output_filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
linear_aec_output_file_writer_.reset(
|
||||||
|
new WavWriter(filename, 16000, out_config_.num_channels()));
|
||||||
|
|
||||||
|
linear_aec_output_buf_.resize(out_config_.num_channels());
|
||||||
|
}
|
||||||
|
|
||||||
if (settings_.reverse_output_filename) {
|
if (settings_.reverse_output_filename) {
|
||||||
std::string filename;
|
std::string filename;
|
||||||
if (settings_.store_intermediate_output) {
|
if (settings_.store_intermediate_output) {
|
||||||
|
@ -410,6 +439,8 @@ void AudioProcessingSimulator::CreateAudioProcessor() {
|
||||||
apm_config.echo_canceller.mobile_mode = use_aecm;
|
apm_config.echo_canceller.mobile_mode = use_aecm;
|
||||||
apm_config.echo_canceller.use_legacy_aec = use_legacy_aec;
|
apm_config.echo_canceller.use_legacy_aec = use_legacy_aec;
|
||||||
}
|
}
|
||||||
|
apm_config.echo_canceller.export_linear_aec_output =
|
||||||
|
!!settings_.linear_aec_output_filename;
|
||||||
|
|
||||||
RTC_CHECK(!(use_legacy_aec && settings_.aec_settings_filename))
|
RTC_CHECK(!(use_legacy_aec && settings_.aec_settings_filename))
|
||||||
<< "The legacy AEC cannot be configured using settings";
|
<< "The legacy AEC cannot be configured using settings";
|
||||||
|
@ -421,9 +452,14 @@ void AudioProcessingSimulator::CreateAudioProcessor() {
|
||||||
std::cout << "Reading AEC Parameters from JSON input." << std::endl;
|
std::cout << "Reading AEC Parameters from JSON input." << std::endl;
|
||||||
}
|
}
|
||||||
cfg = ReadAec3ConfigFromJsonFile(*settings_.aec_settings_filename);
|
cfg = ReadAec3ConfigFromJsonFile(*settings_.aec_settings_filename);
|
||||||
echo_control_factory.reset(new EchoCanceller3Factory(cfg));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_.print_aec_parameter_values) {
|
||||||
if (!settings_.use_quiet_output) {
|
if (!settings_.use_quiet_output) {
|
||||||
std::cout << "AEC settings:" << std::endl;
|
std::cout << "AEC settings:" << std::endl;
|
||||||
|
|
|
@ -47,6 +47,7 @@ struct SimulationSettings {
|
||||||
absl::optional<std::string> input_filename;
|
absl::optional<std::string> input_filename;
|
||||||
absl::optional<std::string> reverse_input_filename;
|
absl::optional<std::string> reverse_input_filename;
|
||||||
absl::optional<std::string> artificial_nearend_filename;
|
absl::optional<std::string> artificial_nearend_filename;
|
||||||
|
absl::optional<std::string> linear_aec_output_filename;
|
||||||
absl::optional<bool> use_aec;
|
absl::optional<bool> use_aec;
|
||||||
absl::optional<bool> use_aecm;
|
absl::optional<bool> use_aecm;
|
||||||
absl::optional<bool> use_ed; // Residual Echo Detector.
|
absl::optional<bool> use_ed; // Residual Echo Detector.
|
||||||
|
@ -156,6 +157,7 @@ class AudioProcessingSimulator {
|
||||||
std::unique_ptr<ChannelBuffer<float>> out_buf_;
|
std::unique_ptr<ChannelBuffer<float>> out_buf_;
|
||||||
std::unique_ptr<ChannelBuffer<float>> reverse_in_buf_;
|
std::unique_ptr<ChannelBuffer<float>> reverse_in_buf_;
|
||||||
std::unique_ptr<ChannelBuffer<float>> reverse_out_buf_;
|
std::unique_ptr<ChannelBuffer<float>> reverse_out_buf_;
|
||||||
|
std::vector<std::array<float, 160>> linear_aec_output_buf_;
|
||||||
StreamConfig in_config_;
|
StreamConfig in_config_;
|
||||||
StreamConfig out_config_;
|
StreamConfig out_config_;
|
||||||
StreamConfig reverse_in_config_;
|
StreamConfig reverse_in_config_;
|
||||||
|
@ -178,6 +180,7 @@ class AudioProcessingSimulator {
|
||||||
std::unique_ptr<ChannelBufferWavWriter> buffer_file_writer_;
|
std::unique_ptr<ChannelBufferWavWriter> buffer_file_writer_;
|
||||||
std::unique_ptr<ChannelBufferWavWriter> reverse_buffer_file_writer_;
|
std::unique_ptr<ChannelBufferWavWriter> reverse_buffer_file_writer_;
|
||||||
std::unique_ptr<ChannelBufferVectorWriter> buffer_memory_writer_;
|
std::unique_ptr<ChannelBufferVectorWriter> buffer_memory_writer_;
|
||||||
|
std::unique_ptr<WavWriter> linear_aec_output_file_writer_;
|
||||||
ApiCallStatistics api_call_statistics_;
|
ApiCallStatistics api_call_statistics_;
|
||||||
std::ofstream residual_echo_likelihood_graph_writer_;
|
std::ofstream residual_echo_likelihood_graph_writer_;
|
||||||
int analog_mic_level_;
|
int analog_mic_level_;
|
||||||
|
|
|
@ -40,6 +40,7 @@ ABSL_FLAG(std::string,
|
||||||
artificial_nearend,
|
artificial_nearend,
|
||||||
"",
|
"",
|
||||||
"Artificial nearend wav filename");
|
"Artificial nearend wav filename");
|
||||||
|
ABSL_FLAG(std::string, linear_aec_output, "", "Linear AEC output wav filename");
|
||||||
ABSL_FLAG(int,
|
ABSL_FLAG(int,
|
||||||
output_num_channels,
|
output_num_channels,
|
||||||
kParameterNotSpecifiedValue,
|
kParameterNotSpecifiedValue,
|
||||||
|
@ -364,6 +365,8 @@ SimulationSettings CreateSettings() {
|
||||||
&settings.reverse_output_filename);
|
&settings.reverse_output_filename);
|
||||||
SetSettingIfSpecified(absl::GetFlag(FLAGS_artificial_nearend),
|
SetSettingIfSpecified(absl::GetFlag(FLAGS_artificial_nearend),
|
||||||
&settings.artificial_nearend_filename);
|
&settings.artificial_nearend_filename);
|
||||||
|
SetSettingIfSpecified(absl::GetFlag(FLAGS_linear_aec_output),
|
||||||
|
&settings.linear_aec_output_filename);
|
||||||
SetSettingIfSpecified(absl::GetFlag(FLAGS_output_num_channels),
|
SetSettingIfSpecified(absl::GetFlag(FLAGS_output_num_channels),
|
||||||
&settings.output_num_channels);
|
&settings.output_num_channels);
|
||||||
SetSettingIfSpecified(absl::GetFlag(FLAGS_reverse_output_num_channels),
|
SetSettingIfSpecified(absl::GetFlag(FLAGS_reverse_output_num_channels),
|
||||||
|
@ -508,6 +511,19 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) {
|
||||||
"aec dump input string!\n");
|
"aec dump input string!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ReportConditionalErrorAndExit(settings.use_aec && !(*settings.use_aec) &&
|
||||||
|
settings.linear_aec_output_filename,
|
||||||
|
"Error: The linear AEC ouput filename cannot "
|
||||||
|
"be specified without the AEC being active");
|
||||||
|
|
||||||
|
ReportConditionalErrorAndExit(
|
||||||
|
((settings.use_aec && *settings.use_aec && settings.use_legacy_aec &&
|
||||||
|
*settings.use_legacy_aec) ||
|
||||||
|
(settings.use_aecm && *settings.use_aecm)) &&
|
||||||
|
!!settings.linear_aec_output_filename,
|
||||||
|
"Error: The linear AEC ouput filename cannot be specified when the "
|
||||||
|
"legacy AEC or the AECm are used");
|
||||||
|
|
||||||
ReportConditionalErrorAndExit(
|
ReportConditionalErrorAndExit(
|
||||||
settings.use_aec && *settings.use_aec && settings.use_aecm &&
|
settings.use_aec && *settings.use_aec && settings.use_aecm &&
|
||||||
*settings.use_aecm,
|
*settings.use_aecm,
|
||||||
|
@ -617,6 +633,11 @@ void PerformBasicParameterSanityChecks(const SimulationSettings& settings) {
|
||||||
!valid_wav_name(*settings.artificial_nearend_filename),
|
!valid_wav_name(*settings.artificial_nearend_filename),
|
||||||
"Error: --artifical_nearend must be a valid .wav file name.\n");
|
"Error: --artifical_nearend must be a valid .wav file name.\n");
|
||||||
|
|
||||||
|
ReportConditionalErrorAndExit(
|
||||||
|
settings.linear_aec_output_filename &&
|
||||||
|
(!valid_wav_name(*settings.linear_aec_output_filename)),
|
||||||
|
"Error: --linear_aec_output must be a valid .wav file name.\n");
|
||||||
|
|
||||||
ReportConditionalErrorAndExit(
|
ReportConditionalErrorAndExit(
|
||||||
WEBRTC_APM_DEBUG_DUMP == 0 && settings.dump_internal_data,
|
WEBRTC_APM_DEBUG_DUMP == 0 && settings.dump_internal_data,
|
||||||
"Error: --dump_data cannot be set without proper build support.\n");
|
"Error: --dump_data cannot be set without proper build support.\n");
|
||||||
|
|
|
@ -24,6 +24,10 @@ class MockEchoControl : public EchoControl {
|
||||||
MOCK_METHOD1(AnalyzeCapture, void(AudioBuffer* capture));
|
MOCK_METHOD1(AnalyzeCapture, void(AudioBuffer* capture));
|
||||||
MOCK_METHOD2(ProcessCapture,
|
MOCK_METHOD2(ProcessCapture,
|
||||||
void(AudioBuffer* capture, bool echo_path_change));
|
void(AudioBuffer* capture, bool echo_path_change));
|
||||||
|
MOCK_METHOD3(ProcessCapture,
|
||||||
|
void(AudioBuffer* capture,
|
||||||
|
AudioBuffer* linear_output,
|
||||||
|
bool echo_path_change));
|
||||||
MOCK_CONST_METHOD0(GetMetrics, EchoControl::Metrics());
|
MOCK_CONST_METHOD0(GetMetrics, EchoControl::Metrics());
|
||||||
MOCK_METHOD1(SetAudioBufferDelay, void(int delay_ms));
|
MOCK_METHOD1(SetAudioBufferDelay, void(int delay_ms));
|
||||||
MOCK_CONST_METHOD0(ActiveProcessing, bool());
|
MOCK_CONST_METHOD0(ActiveProcessing, bool());
|
||||||
|
|
Loading…
Reference in a new issue