blob: b770ab6c09123b997636234e5227f4f1621051f1 [file] [log] [blame]
/* Copyright (c) 2018 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "api/audio/echo_canceller3_factory.h"
#include "modules/audio_processing/aec_dump/aec_dump_factory.h"
#include "modules/audio_processing/include/aec_dump.h"
#include "modules/audio_processing/include/audio_processing.h"
#include "modules/include/module_common_types.h"
#include "rtc_base/task_queue.h"
extern "C" {
#include "webrtc_apm.h"
int convert_to_aec3_config(
const struct aec_config *config,
webrtc::EchoCanceller3Config *aec3_config)
{
aec3_config->delay = {
config->delay.default_delay,
config->delay.down_sampling_factor,
config->delay.num_filters,
config->delay.api_call_jitter_blocks,
config->delay.min_echo_path_delay_blocks,
config->delay.delay_headroom_blocks,
config->delay.hysteresis_limit_1_blocks,
config->delay.hysteresis_limit_2_blocks,
config->delay.skew_hysteresis_blocks
};
aec3_config->filter.main = {
config->filter.main.length_blocks,
config->filter.main.leakage_converged,
config->filter.main.leakage_diverged,
config->filter.main.error_floor,
config->filter.main.noise_gate
};
aec3_config->filter.shadow = {
config->filter.shadow.length_blocks,
config->filter.shadow.rate,
config->filter.shadow.noise_gate
};
aec3_config->filter.main_initial = {
config->filter.main_initial.length_blocks,
config->filter.main_initial.leakage_converged,
config->filter.main_initial.leakage_diverged,
config->filter.main_initial.error_floor,
config->filter.main_initial.noise_gate
};
aec3_config->filter.shadow_initial = {
config->filter.shadow_initial.length_blocks,
config->filter.shadow_initial.rate,
config->filter.shadow_initial.noise_gate
};
aec3_config->filter.config_change_duration_blocks =
config->filter.config_change_duration_blocks;
aec3_config->erle = {
config->erle.min,
config->erle.max_l,
config->erle.max_h,
};
aec3_config->ep_strength = {
config->ep_strength.lf,
config->ep_strength.mf,
config->ep_strength.hf,
config->ep_strength.default_len,
static_cast<bool>(config->ep_strength.echo_can_saturate),
static_cast<bool>(config->ep_strength.bounded_erl)
};
aec3_config->gain_mask.m1 = config->gain_mask.m1;
aec3_config->gain_mask.m2 = config->gain_mask.m2;
aec3_config->gain_mask.m3 = config->gain_mask.m3;
aec3_config->gain_mask.m5 = config->gain_mask.m5;
aec3_config->gain_mask.m6 = config->gain_mask.m6;
aec3_config->gain_mask.m7 = config->gain_mask.m7;
aec3_config->gain_mask.m8 = config->gain_mask.m8;
aec3_config->gain_mask.m9 = config->gain_mask.m9;
aec3_config->gain_mask.gain_curve_offset =
config->gain_mask.gain_curve_offset;
aec3_config->gain_mask.gain_curve_slope =
config->gain_mask.gain_curve_slope;
aec3_config->gain_mask.temporal_masking_lf =
config->gain_mask.temporal_masking_lf;
aec3_config->gain_mask.temporal_masking_hf =
config->gain_mask.temporal_masking_hf;
aec3_config->gain_mask.temporal_masking_lf_bands =
config->gain_mask.temporal_masking_lf_bands;
aec3_config->echo_audibility = {
config->echo_audibility.low_render_limit,
config->echo_audibility.normal_render_limit,
config->echo_audibility.floor_power,
config->echo_audibility.audibility_threshold_lf,
config->echo_audibility.audibility_threshold_mf,
config->echo_audibility.audibility_threshold_hf,
static_cast<bool>(
config->echo_audibility.use_stationary_properties)
};
aec3_config->render_levels = {
config->render_levels.active_render_limit,
config->render_levels.poor_excitation_render_limit,
};
aec3_config->gain_updates.low_noise = {
config->gain_updates.low_noise.max_inc,
config->gain_updates.low_noise.max_dec,
config->gain_updates.low_noise.rate_inc,
config->gain_updates.low_noise.rate_dec,
config->gain_updates.low_noise.min_inc,
config->gain_updates.low_noise.min_dec,
};
aec3_config->gain_updates.initial = {
config->gain_updates.initial.max_inc,
config->gain_updates.initial.max_dec,
config->gain_updates.initial.rate_inc,
config->gain_updates.initial.rate_dec,
config->gain_updates.initial.min_inc,
config->gain_updates.initial.min_dec,
};
aec3_config->gain_updates.normal = {
config->gain_updates.normal.max_inc,
config->gain_updates.normal.max_dec,
config->gain_updates.normal.rate_inc,
config->gain_updates.normal.rate_dec,
config->gain_updates.normal.min_inc,
config->gain_updates.normal.min_dec,
};
aec3_config->gain_updates.saturation = {
config->gain_updates.saturation.max_inc,
config->gain_updates.saturation.max_dec,
config->gain_updates.saturation.rate_inc,
config->gain_updates.saturation.rate_dec,
config->gain_updates.saturation.min_inc,
config->gain_updates.saturation.min_dec,
};
aec3_config->gain_updates.nonlinear = {
config->gain_updates.nonlinear.max_inc,
config->gain_updates.nonlinear.max_dec,
config->gain_updates.nonlinear.rate_inc,
config->gain_updates.nonlinear.rate_dec,
config->gain_updates.nonlinear.min_inc,
config->gain_updates.nonlinear.min_dec,
};
aec3_config->gain_updates.floor_first_increase =
config->gain_updates.floor_first_increase;
aec3_config->echo_removal_control = {
{
config->echo_removal_control.gain_rampup.first_non_zero_gain,
config->echo_removal_control.gain_rampup.non_zero_gain_blocks,
config->echo_removal_control.gain_rampup.full_gain_blocks
},
static_cast<bool>(config->echo_removal_control.has_clock_drift)
};
aec3_config->echo_model = {
config->echo_model.noise_floor_hold,
config->echo_model.min_noise_floor_power,
config->echo_model.stationary_gate_slope,
config->echo_model.noise_gate_power,
config->echo_model.noise_gate_slope,
config->echo_model.render_pre_window_size,
config->echo_model.render_post_window_size,
config->echo_model.nonlinear_hold,
config->echo_model.nonlinear_release
};
return 0;
}
webrtc_apm webrtc_apm_create(unsigned int num_channels,
unsigned int frame_rate,
struct aec_config *config)
{
int err;
webrtc::AudioProcessing *apm;
webrtc::AudioProcessing::ChannelLayout channel_layout;
webrtc::AudioProcessingBuilder apm_builder;
webrtc::EchoCanceller3Config aec3_config;
std::unique_ptr<webrtc::EchoControlFactory> ec3_factory;
if (config) {
convert_to_aec3_config(config, &aec3_config);
ec3_factory.reset(
new webrtc::EchoCanceller3Factory(aec3_config));
} else {
ec3_factory.reset(new webrtc::EchoCanceller3Factory());
}
switch (num_channels) {
case 1:
channel_layout = webrtc::AudioProcessing::kMono;
break;
case 2:
channel_layout = webrtc::AudioProcessing::kStereo;
break;
default:
return NULL;
}
apm_builder.SetEchoControlFactory(std::move(ec3_factory));
apm = apm_builder.Create();
err = apm->Initialize(frame_rate, frame_rate, frame_rate,
channel_layout, channel_layout, channel_layout);
if (err) {
delete apm;
return NULL;
}
return reinterpret_cast<webrtc_apm>(apm);
}
int webrtc_apm_process_reverse_stream_f(
webrtc_apm ptr,
int num_channels, int rate,
float *const *data)
{
webrtc::AudioProcessing *apm;
webrtc::StreamConfig config =
webrtc::StreamConfig(rate, num_channels);
apm = reinterpret_cast<webrtc::AudioProcessing *>(ptr);
return apm->ProcessReverseStream(data, config, config, data);
}
int webrtc_apm_process_reverse_stream(webrtc_apm ptr,
int num_channels, int rate,
int16_t *data, int nframes)
{
webrtc::AudioFrame af;
webrtc::AudioProcessing *apm;
apm = reinterpret_cast<webrtc::AudioProcessing *>(ptr);
af.UpdateFrame(0xFFFFFFFF, data, nframes, rate,
webrtc::AudioFrame::kNormalSpeech,
webrtc::AudioFrame::kVadUnknown,
num_channels);
return apm->ProcessReverseStream(&af);
}
int webrtc_apm_process_stream_f(webrtc_apm ptr,
int num_channels,
int rate,
float *const *data)
{
webrtc::AudioProcessing *apm;
webrtc::StreamConfig config =
webrtc::StreamConfig(rate, num_channels);
apm = reinterpret_cast<webrtc::AudioProcessing *>(ptr);
return apm->ProcessStream(data, config, config, data);
}
int webrtc_apm_process_stream(webrtc_apm ptr, int num_channels,
int rate, int16_t *data, int nframes)
{
int ret;
webrtc::AudioFrame af;
webrtc::AudioProcessing *apm;
apm = reinterpret_cast<webrtc::AudioProcessing *>(ptr);
//set stream delay
af.UpdateFrame(0xFFFFFFFF, data, nframes, rate,
webrtc::AudioFrame::kNormalSpeech,
webrtc::AudioFrame::kVadUnknown,
num_channels);
ret = apm->ProcessStream(&af);
if (ret)
return ret;
memcpy(data, af.data(), nframes * num_channels * 2);
return ret;
}
void webrtc_apm_destroy(webrtc_apm ptr)
{
webrtc::AudioProcessing *apm;
apm = reinterpret_cast<webrtc::AudioProcessing *>(ptr);
delete apm;
}
int webrtc_apm_set_stream_delay(webrtc_apm ptr, int delay_ms)
{
webrtc::AudioProcessing *apm;
apm = reinterpret_cast<webrtc::AudioProcessing *>(ptr);
return apm->set_stream_delay_ms(delay_ms);
}
int webrtc_apm_aec_dump(webrtc_apm ptr, void** wq_ptr, int start, FILE *handle)
{
webrtc::AudioProcessing *apm;
rtc::TaskQueue *work_queue;
apm = reinterpret_cast<webrtc::AudioProcessing *>(ptr);
if (start) {
work_queue = new rtc::TaskQueue("aecdump-worker-queue",
rtc::TaskQueue::Priority::LOW);
auto aec_dump = webrtc::AecDumpFactory::Create(handle, -1, work_queue);
if (!aec_dump)
return -ENOMEM;
apm->AttachAecDump(std::move(aec_dump));
*wq_ptr = reinterpret_cast<void *>(work_queue);
} else {
apm->DetachAecDump();
work_queue = reinterpret_cast<rtc::TaskQueue *>(*wq_ptr);
if (work_queue) {
delete work_queue;
work_queue = NULL;
}
}
return 0;
}
int webrtc_apm_has_echo(webrtc_apm ptr)
{
webrtc::AudioProcessing *apm;
apm = reinterpret_cast<webrtc::AudioProcessing *>(ptr);
return apm->echo_cancellation()->stream_has_echo();
}
} // extern "C"