2019-06-04 10:14:50 +00:00
|
|
|
//
|
|
|
|
// libtgvoip is free and unencumbered public domain software.
|
|
|
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
|
|
|
// you should have received with this source code distribution.
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef TGVOIP_NO_DSP
|
2020-08-14 16:58:22 +00:00
|
|
|
#include "modules/audio_processing/include/audio_processing.h"
|
|
|
|
#include "api/audio/audio_frame.h"
|
2019-06-04 10:14:50 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "EchoCanceller.h"
|
|
|
|
#include "audio/AudioOutput.h"
|
|
|
|
#include "audio/AudioInput.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "VoIPServerConfig.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
using namespace tgvoip;
|
|
|
|
|
|
|
|
EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC){
|
|
|
|
#ifndef TGVOIP_NO_DSP
|
|
|
|
this->enableAEC=enableAEC;
|
|
|
|
this->enableAGC=enableAGC;
|
|
|
|
this->enableNS=enableNS;
|
|
|
|
isOn=true;
|
|
|
|
|
|
|
|
webrtc::Config extraConfig;
|
|
|
|
|
|
|
|
apm=webrtc::AudioProcessingBuilder().Create(extraConfig);
|
|
|
|
|
|
|
|
webrtc::AudioProcessing::Config config;
|
|
|
|
config.echo_canceller.enabled = enableAEC;
|
|
|
|
#ifndef TGVOIP_USE_DESKTOP_DSP
|
|
|
|
config.echo_canceller.mobile_mode = true;
|
|
|
|
#else
|
|
|
|
config.echo_canceller.mobile_mode = false;
|
|
|
|
#endif
|
|
|
|
config.high_pass_filter.enabled = enableAEC;
|
|
|
|
config.gain_controller2.enabled = enableAGC;
|
2020-08-14 16:58:22 +00:00
|
|
|
|
|
|
|
using Level = webrtc::AudioProcessing::Config::NoiseSuppression::Level;
|
|
|
|
Level nsLevel;
|
2019-06-04 10:14:50 +00:00
|
|
|
#ifdef __APPLE__
|
|
|
|
switch(ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level_vpio", 0)){
|
|
|
|
#else
|
2020-08-14 16:58:22 +00:00
|
|
|
switch (ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level", 2)) {
|
2019-06-04 10:14:50 +00:00
|
|
|
#endif
|
2020-08-14 16:58:22 +00:00
|
|
|
case 0:
|
|
|
|
nsLevel=Level::kLow;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
nsLevel=Level::kModerate;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
nsLevel=Level::kVeryHigh;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
default:
|
|
|
|
nsLevel=Level::kHigh;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
config.noise_suppression.level = nsLevel;
|
|
|
|
config.noise_suppression.enabled = enableNS;
|
|
|
|
if (enableAGC) {
|
|
|
|
config.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::kAdaptiveDigital;
|
|
|
|
config.gain_controller1.target_level_dbfs = ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_target_level", 9);
|
|
|
|
config.gain_controller1.enable_limiter = ServerConfig::GetSharedInstance()->GetBoolean("webrtc_agc_enable_limiter", true);
|
|
|
|
config.gain_controller1.compression_gain_db = ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_compression_gain", 20);
|
|
|
|
}
|
|
|
|
config.voice_detection.enabled = true;
|
|
|
|
|
|
|
|
apm->ApplyConfig(config);
|
|
|
|
|
|
|
|
audioFrame = new webrtc::AudioFrame();
|
|
|
|
audioFrame->samples_per_channel_ = 480;
|
|
|
|
audioFrame->sample_rate_hz_ = 48000;
|
|
|
|
audioFrame->num_channels_ = 1;
|
|
|
|
|
|
|
|
farendQueue = new BlockingQueue<int16_t *>(11);
|
|
|
|
farendBufferPool = new BufferPool(960 * 2, 10);
|
|
|
|
running = true;
|
|
|
|
bufferFarendThread = new Thread(std::bind(&EchoCanceller::RunBufferFarendThread, this));
|
|
|
|
bufferFarendThread->Start();
|
2019-06-04 10:14:50 +00:00
|
|
|
|
|
|
|
#else
|
2020-08-14 16:58:22 +00:00
|
|
|
this->enableAEC=this->enableAGC=enableAGC=this->enableNS=enableNS=false;
|
|
|
|
isOn=true;
|
2019-06-04 10:14:50 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
EchoCanceller::~EchoCanceller() {
|
2019-06-04 10:14:50 +00:00
|
|
|
#ifndef TGVOIP_NO_DSP
|
2020-08-14 16:58:22 +00:00
|
|
|
delete apm;
|
|
|
|
delete audioFrame;
|
|
|
|
delete farendBufferPool;
|
2019-06-04 10:14:50 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void EchoCanceller::Start() {
|
2019-06-04 10:14:50 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void EchoCanceller::Stop() {
|
2019-06-04 10:14:50 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len) {
|
|
|
|
if (len != 960 * 2 || !enableAEC || !isOn)
|
|
|
|
return;
|
2019-06-04 10:14:50 +00:00
|
|
|
#ifndef TGVOIP_NO_DSP
|
2020-08-14 16:58:22 +00:00
|
|
|
int16_t *buf = (int16_t *) farendBufferPool->Get();
|
|
|
|
if (buf) {
|
|
|
|
memcpy(buf, data, 960 * 2);
|
|
|
|
farendQueue->Put(buf);
|
|
|
|
}
|
2019-06-04 10:14:50 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef TGVOIP_NO_DSP
|
2020-08-14 16:58:22 +00:00
|
|
|
void EchoCanceller::RunBufferFarendThread() {
|
|
|
|
webrtc::AudioFrame frame;
|
|
|
|
frame.num_channels_ = 1;
|
|
|
|
frame.sample_rate_hz_ = 48000;
|
|
|
|
frame.samples_per_channel_ = 480;
|
|
|
|
|
|
|
|
webrtc::StreamConfig input_config(frame.sample_rate_hz_, frame.num_channels_,
|
|
|
|
/*has_keyboard=*/false);
|
|
|
|
webrtc::StreamConfig output_config(frame.sample_rate_hz_, frame.num_channels_,
|
|
|
|
/*has_keyboard=*/false);
|
|
|
|
|
|
|
|
while (running) {
|
|
|
|
int16_t *samplesIn = farendQueue->GetBlocking();
|
|
|
|
if (samplesIn) {
|
|
|
|
memcpy(frame.mutable_data(), samplesIn, 480 * 2);
|
|
|
|
apm->ProcessReverseStream(frame.data(), input_config,
|
|
|
|
output_config, frame.mutable_data());
|
|
|
|
memcpy(frame.mutable_data(), samplesIn + 480, 480 * 2);
|
|
|
|
apm->ProcessReverseStream(frame.data(), input_config,
|
|
|
|
output_config, frame.mutable_data());
|
|
|
|
didBufferFarend = true;
|
|
|
|
farendBufferPool->Reuse(reinterpret_cast<unsigned char *>(samplesIn));
|
|
|
|
}
|
|
|
|
}
|
2019-06-04 10:14:50 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void EchoCanceller::Enable(bool enabled) {
|
|
|
|
isOn = enabled;
|
2019-06-04 10:14:50 +00:00
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void EchoCanceller::ProcessInput(int16_t* inOut, size_t numSamples, bool& hasVoice) {
|
2019-06-04 10:14:50 +00:00
|
|
|
#ifndef TGVOIP_NO_DSP
|
2020-08-14 16:58:22 +00:00
|
|
|
if (!isOn || (!enableAEC && !enableAGC && !enableNS)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int delay = audio::AudioInput::GetEstimatedDelay() + audio::AudioOutput::GetEstimatedDelay();
|
|
|
|
assert(numSamples == 960);
|
|
|
|
|
|
|
|
webrtc::StreamConfig input_config(audioFrame->sample_rate_hz_, audioFrame->num_channels_,
|
|
|
|
/*has_keyboard=*/false);
|
|
|
|
webrtc::StreamConfig output_config(audioFrame->sample_rate_hz_, audioFrame->num_channels_,
|
|
|
|
/*has_keyboard=*/false);
|
|
|
|
|
|
|
|
memcpy(audioFrame->mutable_data(), inOut, 480 * 2);
|
|
|
|
if (enableAEC)
|
|
|
|
apm->set_stream_delay_ms(delay);
|
|
|
|
apm->ProcessStream(audioFrame->data(), input_config,
|
|
|
|
output_config, audioFrame->mutable_data());
|
|
|
|
if (enableVAD)
|
|
|
|
hasVoice= apm->GetStatistics().voice_detected.value_or(false);
|
|
|
|
memcpy(inOut, audioFrame->data(), 480 * 2);
|
|
|
|
memcpy(audioFrame->mutable_data(), inOut + 480, 480 * 2);
|
|
|
|
if (enableAEC)
|
|
|
|
apm->set_stream_delay_ms(delay);
|
|
|
|
apm->ProcessStream(audioFrame->data(), input_config,
|
|
|
|
output_config, audioFrame->mutable_data());
|
|
|
|
if (enableVAD) {
|
|
|
|
hasVoice=hasVoice || apm->GetStatistics().voice_detected.value_or(false);
|
|
|
|
}
|
|
|
|
memcpy(inOut + 480, audioFrame->data(), 480 * 2);
|
2019-06-04 10:14:50 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void EchoCanceller::SetAECStrength(int strength) {
|
2019-06-04 10:14:50 +00:00
|
|
|
#ifndef TGVOIP_NO_DSP
|
2020-08-14 16:58:22 +00:00
|
|
|
/*if(aec){
|
2019-06-04 10:14:50 +00:00
|
|
|
#ifndef TGVOIP_USE_DESKTOP_DSP
|
2020-08-14 16:58:22 +00:00
|
|
|
AecmConfig cfg;
|
|
|
|
cfg.cngMode=AecmFalse;
|
|
|
|
cfg.echoMode=(int16_t) strength;
|
|
|
|
WebRtcAecm_set_config(aec, cfg);
|
2019-06-04 10:14:50 +00:00
|
|
|
#endif
|
2020-08-14 16:58:22 +00:00
|
|
|
}*/
|
2019-06-04 10:14:50 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void EchoCanceller::SetVoiceDetectionEnabled(bool enabled) {
|
|
|
|
enableVAD = enabled;
|
2019-06-04 10:14:50 +00:00
|
|
|
#ifndef TGVOIP_NO_DSP
|
2020-08-14 16:58:22 +00:00
|
|
|
auto config = apm->GetConfig();
|
|
|
|
config.voice_detection.enabled = enabled;
|
|
|
|
apm->ApplyConfig(config);
|
2019-06-04 10:14:50 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
using namespace tgvoip::effects;
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
AudioEffect::~AudioEffect() {
|
2019-06-04 10:14:50 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void AudioEffect::SetPassThrough(bool passThrough) {
|
|
|
|
this->passThrough = passThrough;
|
2019-06-04 10:14:50 +00:00
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
Volume::Volume() {
|
2019-06-04 10:14:50 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
Volume::~Volume() {
|
2019-06-04 10:14:50 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void Volume::Process(int16_t* inOut, size_t numSamples) {
|
|
|
|
if (level == 1.0f || passThrough) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (size_t i = 0; i < numSamples; i++) {
|
|
|
|
float sample = (float) inOut[i] * multiplier;
|
|
|
|
if (sample > 32767.0f)
|
|
|
|
inOut[i] = INT16_MAX;
|
|
|
|
else if (sample < -32768.0f)
|
|
|
|
inOut[i] = INT16_MIN;
|
|
|
|
else
|
|
|
|
inOut[i] = (int16_t) sample;
|
|
|
|
}
|
2019-06-04 10:14:50 +00:00
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
void Volume::SetLevel(float level) {
|
|
|
|
this->level = level;
|
|
|
|
float db;
|
|
|
|
if (level < 1.0f)
|
|
|
|
db = -50.0f * (1.0f - level);
|
|
|
|
else if (level > 1.0f && level <= 2.0f)
|
|
|
|
db = 10.0f * (level - 1.0f);
|
|
|
|
else
|
|
|
|
db = 0.0f;
|
|
|
|
multiplier = expf(db / 20.0f * logf(10.0f));
|
2019-06-04 10:14:50 +00:00
|
|
|
}
|
|
|
|
|
2020-08-14 16:58:22 +00:00
|
|
|
float Volume::GetLevel() {
|
|
|
|
return level;
|
2019-06-04 10:14:50 +00:00
|
|
|
}
|