Nagram/TMessagesProj/jni/libtgvoip3/EchoCanceller.cpp
2020-04-24 12:21:58 +03:00

265 lines
7.1 KiB
C++

//
// 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.
//
#include "logging.h"
#include "EchoCanceller.h"
#include "VoIPServerConfig.h"
#include "audio/AudioInput.h"
#include "audio/AudioOutput.h"
#ifndef TGVOIP_NO_DSP
#include "webrtc_dsp/api/audio/audio_frame.h"
#include "webrtc_dsp/modules/audio_processing/include/audio_processing.h"
#endif
#include <cmath>
#include <cstdio>
#include <cstring>
#include <limits>
using namespace tgvoip;
EchoCanceller::EchoCanceller(bool enableAEC, bool enableNS, bool enableAGC)
: m_enableAEC(enableAEC)
, m_enableAGC(enableAGC)
, m_enableNS(enableNS)
, m_isOn(true)
{
#ifndef TGVOIP_NO_DSP
webrtc::Config extraConfig;
#ifdef TGVOIP_USE_DESKTOP_DSP
extraConfig.Set(new webrtc::DelayAgnostic(true));
#endif
m_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;
m_apm->ApplyConfig(config);
webrtc::NoiseSuppression::Level nsLevel;
#ifdef __APPLE__
switch (ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level_vpio", 0))
{
#else
switch (ServerConfig::GetSharedInstance()->GetInt("webrtc_ns_level", 2))
{
#endif
case 0:
nsLevel = webrtc::NoiseSuppression::Level::kLow;
break;
case 1:
nsLevel = webrtc::NoiseSuppression::Level::kModerate;
break;
case 3:
nsLevel = webrtc::NoiseSuppression::Level::kVeryHigh;
break;
case 2:
default:
nsLevel = webrtc::NoiseSuppression::Level::kHigh;
break;
}
m_apm->noise_suppression()->set_level(nsLevel);
m_apm->noise_suppression()->Enable(enableNS);
if (enableAGC)
{
m_apm->gain_control()->set_mode(webrtc::GainControl::Mode::kAdaptiveDigital);
m_apm->gain_control()->set_target_level_dbfs(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_target_level", 9));
m_apm->gain_control()->enable_limiter(ServerConfig::GetSharedInstance()->GetBoolean("webrtc_agc_enable_limiter", true));
m_apm->gain_control()->set_compression_gain_db(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_compression_gain", 20));
}
m_apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kVeryLowLikelihood);
m_audioFrame = new webrtc::AudioFrame();
m_audioFrame->samples_per_channel_ = 480;
m_audioFrame->sample_rate_hz_ = 48000;
m_audioFrame->num_channels_ = 1;
m_farendQueue = new BlockingQueue<Buffer>(11);
m_running = true;
m_bufferFarendThread = new Thread(std::bind(&EchoCanceller::RunBufferFarendThread, this));
m_bufferFarendThread->SetName("VoipECBufferFarEnd");
m_bufferFarendThread->Start();
#else
this->enableAEC = this->enableAGC = enableAGC = this->enableNS = enableNS = false;
isOn = true;
#endif
}
EchoCanceller::~EchoCanceller()
{
#ifndef TGVOIP_NO_DSP
m_farendQueue->Put(Buffer());
m_bufferFarendThread->Join();
delete m_bufferFarendThread;
delete m_farendQueue;
delete m_audioFrame;
delete m_apm;
#endif
}
void EchoCanceller::Start()
{
}
void EchoCanceller::Stop()
{
}
void EchoCanceller::SpeakerOutCallback(std::uint8_t* data, std::size_t len)
{
if (len != 960 * 2 || !m_enableAEC || !m_isOn)
return;
#ifndef TGVOIP_NO_DSP
try
{
Buffer buf = m_farendBufferPool.Get();
buf.CopyFrom(data, 0, 960 * 2);
m_farendQueue->Put(std::move(buf));
}
catch (const std::bad_alloc& exception)
{
LOGW("Echo canceller can't keep up with real time.\nwhat():\n%s", exception.what());
}
#endif
}
#ifndef TGVOIP_NO_DSP
void EchoCanceller::RunBufferFarendThread()
{
webrtc::AudioFrame frame;
frame.num_channels_ = 1;
frame.sample_rate_hz_ = 48000;
frame.samples_per_channel_ = 480;
while (m_running)
{
Buffer buf = m_farendQueue->GetBlocking();
if (buf.IsEmpty())
{
LOGI("Echo canceller buffer farend thread exiting");
return;
}
std::int16_t* samplesIn = reinterpret_cast<std::int16_t*>(*buf);
std::memcpy(frame.mutable_data(), samplesIn, 480 * 2);
m_apm->ProcessReverseStream(&frame);
std::memcpy(frame.mutable_data(), samplesIn + 480, 480 * 2);
m_apm->ProcessReverseStream(&frame);
m_didBufferFarend = true;
}
}
#endif
void EchoCanceller::Enable(bool enabled)
{
m_isOn = enabled;
}
void EchoCanceller::ProcessInput(std::int16_t* inOut, std::size_t numSamples, bool& hasVoice)
{
#ifndef TGVOIP_NO_DSP
if (!m_isOn || (!m_enableAEC && !m_enableAGC && !m_enableNS))
return;
int delay = audio::AudioInput::GetEstimatedDelay() + audio::AudioOutput::GetEstimatedDelay();
assert(numSamples == 960);
std::memcpy(m_audioFrame->mutable_data(), inOut, 480 * 2);
if (m_enableAEC)
m_apm->set_stream_delay_ms(delay);
m_apm->ProcessStream(m_audioFrame);
if (m_enableVAD)
hasVoice = m_apm->voice_detection()->stream_has_voice();
std::memcpy(inOut, m_audioFrame->data(), 480 * 2);
std::memcpy(m_audioFrame->mutable_data(), inOut + 480, 480 * 2);
if (m_enableAEC)
m_apm->set_stream_delay_ms(delay);
m_apm->ProcessStream(m_audioFrame);
if (m_enableVAD)
hasVoice = hasVoice || m_apm->voice_detection()->stream_has_voice();
std::memcpy(inOut + 480, m_audioFrame->data(), 480 * 2);
#endif
}
void EchoCanceller::SetAECStrength(int strength)
{
#ifndef TGVOIP_NO_DSP
#endif
}
void EchoCanceller::SetVoiceDetectionEnabled(bool enabled)
{
m_enableVAD = enabled;
#ifndef TGVOIP_NO_DSP
m_apm->voice_detection()->Enable(enabled);
#endif
}
using namespace tgvoip::effects;
AudioEffect::~AudioEffect() = default;
void AudioEffect::SetPassThrough(bool passThrough)
{
m_passThrough = passThrough;
}
bool AudioEffect::GetPassThrough() const
{
return m_passThrough;
}
Volume::Volume() = default;
Volume::~Volume() = default;
void Volume::Process(std::int16_t* inOut, std::size_t numSamples) const
{
if (m_level == 1.0f || GetPassThrough())
{
return;
}
for (std::size_t i = 0; i < numSamples; ++i)
{
float sample = inOut[i] * m_multiplier;
if (sample > 32767.0f)
inOut[i] = std::numeric_limits<std::int16_t>::max();
else if (sample < -32768.0f)
inOut[i] = std::numeric_limits<std::int16_t>::min();
else
inOut[i] = static_cast<std::int16_t>(sample);
}
}
void Volume::SetLevel(float level)
{
m_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;
m_multiplier = expf(db / 20.0f * logf(10.0f));
}
float Volume::GetLevel() const
{
return m_level;
}