<commit_before>//
// 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
#include "modules/audio_processing/include/audio_processing.h"
#include "api/audio/audio_frame.h"
#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;
#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
	extraConfig.Set(new webrtc::DelayAgnostic(true));
#endif

	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;

#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
	apm->ApplyConfig(config);

	using Level = webrtc::NoiseSuppression::Level;
#else
	using Level = webrtc::AudioProcessing::Config::NoiseSuppression::Level;
#endif
	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=Level::kLow;
			break;
		case 1:
			nsLevel=Level::kModerate;
			break;
		case 3:
			nsLevel=Level::kVeryHigh;
			break;
		case 2:
		default:
			nsLevel=Level::kHigh;
			break;
	}
#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
	apm->noise_suppression()->set_level(nsLevel);
	apm->noise_suppression()->Enable(enableNS);
	if(enableAGC){
		apm->gain_control()->set_mode(webrtc::GainControl::Mode::kAdaptiveDigital);
		apm->gain_control()->set_target_level_dbfs(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_target_level", 9));
		apm->gain_control()->enable_limiter(ServerConfig::GetSharedInstance()->GetBoolean("webrtc_agc_enable_limiter", true));
		apm->gain_control()->set_compression_gain_db(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_compression_gain", 20));
	}
	apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kVeryLowLikelihood);
#else
	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);
#endif

	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();

#else
	this->enableAEC=this->enableAGC=enableAGC=this->enableNS=enableNS=false;
	isOn=true;
#endif
}

EchoCanceller::~EchoCanceller(){
#ifndef TGVOIP_NO_DSP
	delete apm;
	delete audioFrame;
	delete farendBufferPool;
#endif
}

void EchoCanceller::Start(){

}

void EchoCanceller::Stop(){

}


void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){
	if(len!=960*2 || !enableAEC || !isOn)
		return;
#ifndef TGVOIP_NO_DSP
	int16_t* buf=(int16_t*)farendBufferPool->Get();
	if(buf){
		memcpy(buf, data, 960*2);
		farendQueue->Put(buf);
	}
#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(running){
		int16_t* samplesIn=farendQueue->GetBlocking();
		if(samplesIn){
			memcpy(frame.mutable_data(), samplesIn, 480*2);
			apm->ProcessReverseStream(&frame);
			memcpy(frame.mutable_data(), samplesIn+480, 480*2);
			apm->ProcessReverseStream(&frame);
			didBufferFarend=true;
			farendBufferPool->Reuse(reinterpret_cast<unsigned char*>(samplesIn));
		}
	}
}
#endif

void EchoCanceller::Enable(bool enabled){
	isOn=enabled;
}

void EchoCanceller::ProcessInput(int16_t* inOut, size_t numSamples, bool& hasVoice){
#ifndef TGVOIP_NO_DSP
	if(!isOn || (!enableAEC && !enableAGC && !enableNS)){
		return;
	}
	int delay=audio::AudioInput::GetEstimatedDelay()+audio::AudioOutput::GetEstimatedDelay();
	assert(numSamples==960);

	memcpy(audioFrame->mutable_data(), inOut, 480*2);
	if(enableAEC)
		apm->set_stream_delay_ms(delay);
	apm->ProcessStream(audioFrame);
	if(enableVAD)
#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
		hasVoice=apm->voice_detection()->stream_has_voice();
#else
		hasVoice= apm->GetStatistics().voice_detected.value_or(false);
#endif
	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);
	if(enableVAD){
#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
		hasVoice=hasVoice || apm->voice_detection()->stream_has_voice();
#else<commit_msg>Use methods from updated webrtc.<commit_after>//
// 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
#include "modules/audio_processing/include/audio_processing.h"
#include "modules/audio_processing/include/audio_frame_proxies.h"
#include "api/audio/audio_frame.h"
#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;
#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
	extraConfig.Set(new webrtc::DelayAgnostic(true));
#endif

	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;

#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
	apm->ApplyConfig(config);

	using Level = webrtc::NoiseSuppression::Level;
#else
	using Level = webrtc::AudioProcessing::Config::NoiseSuppression::Level;
#endif
	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=Level::kLow;
			break;
		case 1:
			nsLevel=Level::kModerate;
			break;
		case 3:
			nsLevel=Level::kVeryHigh;
			break;
		case 2:
		default:
			nsLevel=Level::kHigh;
			break;
	}
#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
	apm->noise_suppression()->set_level(nsLevel);
	apm->noise_suppression()->Enable(enableNS);
	if(enableAGC){
		apm->gain_control()->set_mode(webrtc::GainControl::Mode::kAdaptiveDigital);
		apm->gain_control()->set_target_level_dbfs(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_target_level", 9));
		apm->gain_control()->enable_limiter(ServerConfig::GetSharedInstance()->GetBoolean("webrtc_agc_enable_limiter", true));
		apm->gain_control()->set_compression_gain_db(ServerConfig::GetSharedInstance()->GetInt("webrtc_agc_compression_gain", 20));
	}
	apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kVeryLowLikelihood);
#else
	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);
#endif

	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();

#else
	this->enableAEC=this->enableAGC=enableAGC=this->enableNS=enableNS=false;
	isOn=true;
#endif
}

EchoCanceller::~EchoCanceller(){
#ifndef TGVOIP_NO_DSP
	delete apm;
	delete audioFrame;
	delete farendBufferPool;
#endif
}

void EchoCanceller::Start(){

}

void EchoCanceller::Stop(){

}


void EchoCanceller::SpeakerOutCallback(unsigned char* data, size_t len){
	if(len!=960*2 || !enableAEC || !isOn)
		return;
#ifndef TGVOIP_NO_DSP
	int16_t* buf=(int16_t*)farendBufferPool->Get();
	if(buf){
		memcpy(buf, data, 960*2);
		farendQueue->Put(buf);
	}
#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(running){
		int16_t* samplesIn=farendQueue->GetBlocking();
		if(samplesIn){
			memcpy(frame.mutable_data(), samplesIn, 480*2);
			webrtc::ProcessReverseAudioFrame(apm, &frame);
			memcpy(frame.mutable_data(), samplesIn+480, 480*2);
			webrtc::ProcessReverseAudioFrame(apm, &frame);
			didBufferFarend=true;
			farendBufferPool->Reuse(reinterpret_cast<unsigned char*>(samplesIn));
		}
	}
}
#endif

void EchoCanceller::Enable(bool enabled){
	isOn=enabled;
}

void EchoCanceller::ProcessInput(int16_t* inOut, size_t numSamples, bool& hasVoice){
#ifndef TGVOIP_NO_DSP
	if(!isOn || (!enableAEC && !enableAGC && !enableNS)){
		return;
	}
	int delay=audio::AudioInput::GetEstimatedDelay()+audio::AudioOutput::GetEstimatedDelay();
	assert(numSamples==960);

	memcpy(audioFrame->mutable_data(), inOut, 480*2);
	if(enableAEC)
		apm->set_stream_delay_ms(delay);
	webrtc::ProcessAudioFrame(apm, audioFrame);
	if(enableVAD)
#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
		hasVoice=apm->voice_detection()->stream_has_voice();
#else
		hasVoice= apm->GetStatistics().voice_detected.value_or(false);
#endif
	memcpy(inOut, audioFrame->data(), 480*2);
	memcpy(audioFrame->mutable_data(), inOut+480, 480*2);
	if(enableAEC)
		apm->set_stream_delay_ms(delay);
	webrtc::ProcessAudioFrame(apm, audioFrame);
	if(enableVAD){
#ifdef TGVOIP_USE_DESKTOP_DSP_BUNDLED
		hasVoice=hasVoice || apm->voice_detection()->stream_has_voice();
#else
{'fraction_non_alphanumeric': 0.13733831061724108, 'fraction_numerical': 0.015799982637381717, 'mean_word_length': 8.085173501577287, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 113, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3694164', 'n_tokens_mistral': 4559, 'n_tokens_neox': 4242, 'n_words': 587}