244 lines
9.2 KiB
C++
244 lines
9.2 KiB
C++
#include <iostream>
|
|
#include <unistd.h>
|
|
#include <cmath>
|
|
#include "timing.h"
|
|
#include "log/logger.h"
|
|
#include "common.h"
|
|
|
|
#include <modules/audio_processing/include/audio_processing.h>
|
|
#include <modules/audio_processing/include/config.h>
|
|
#include "alsa_dev.h"
|
|
#include "agcm.h"
|
|
|
|
using namespace toolkit;
|
|
|
|
#define MIX_AUDIO_CHANNELS 2
|
|
#define MIX_AUDIO_RATE 48000
|
|
#define MIX_AUDIO_SAMPLES (10 * MIX_AUDIO_RATE / 1000)
|
|
|
|
struct audio_buf_t
|
|
{
|
|
uint8_t* data;
|
|
int index;
|
|
int size;
|
|
};
|
|
|
|
struct CallContext {
|
|
// RtmpConfig rtmp;
|
|
std::thread *rtmp_thread;
|
|
std::thread *alsa_thread;
|
|
std::mutex *mutex;
|
|
std::vector<audio_buf_t> *list;
|
|
webrtc::AudioProcessing *apm_agc;
|
|
webrtc::AudioProcessing *apm_ns;
|
|
webrtc::StreamConfig *rtc_stream_config;
|
|
alsa::AlsaDev *alsa;
|
|
alsa::Config *alsa_config;
|
|
LegacyAgc *agcModule;
|
|
bool running;
|
|
};
|
|
|
|
webrtc::AudioProcessing::Config webtcConfigInit()
|
|
{
|
|
webrtc::AudioProcessing::Config apmConfig;
|
|
apmConfig.pipeline.maximum_internal_processing_rate = MIX_AUDIO_RATE;
|
|
apmConfig.pipeline.multi_channel_capture = true;
|
|
apmConfig.pipeline.multi_channel_render = true;
|
|
//PreAmplifier
|
|
apmConfig.pre_amplifier.enabled = false;
|
|
apmConfig.pre_amplifier.fixed_gain_factor = 0.7f;
|
|
//HighPassFilter
|
|
apmConfig.high_pass_filter.enabled = true;
|
|
apmConfig.high_pass_filter.apply_in_full_band = false;
|
|
//EchoCanceller
|
|
apmConfig.echo_canceller.enabled = false;
|
|
apmConfig.echo_canceller.mobile_mode = false;
|
|
apmConfig.echo_canceller.export_linear_aec_output = false;
|
|
apmConfig.echo_canceller.enforce_high_pass_filtering = false;
|
|
//NoiseSuppression
|
|
apmConfig.noise_suppression.enabled = true;
|
|
apmConfig.noise_suppression.level = webrtc::AudioProcessing::Config::NoiseSuppression::kVeryHigh;
|
|
apmConfig.noise_suppression.analyze_linear_aec_output_when_available = false;
|
|
//TransientSuppression
|
|
apmConfig.transient_suppression.enabled = false;
|
|
//VoiceDetection
|
|
apmConfig.voice_detection.enabled = true;
|
|
//GainController1
|
|
apmConfig.gain_controller1.enabled = true;
|
|
// kAdaptiveAnalog 自适应模拟模式
|
|
// kAdaptiveDigital 自适应数字增益模式
|
|
// kFixedDigital 固定数字增益模式
|
|
apmConfig.gain_controller1.mode = webrtc::AudioProcessing::Config::GainController1::kFixedDigital;
|
|
apmConfig.gain_controller1.target_level_dbfs = 6; // 目标音量
|
|
apmConfig.gain_controller1.compression_gain_db = 60; // 增益能力
|
|
apmConfig.gain_controller1.enable_limiter = true; // 压限器开关
|
|
apmConfig.gain_controller1.analog_level_minimum = 0;
|
|
apmConfig.gain_controller1.analog_level_maximum = 255;
|
|
apmConfig.gain_controller1.analog_gain_controller.enabled = false;
|
|
// apmConfig.gain_controller1.analog_gain_controller.startup_min_volume = webrtc::kAgcStartupMinVolume;
|
|
apmConfig.gain_controller1.analog_gain_controller.startup_min_volume = 0;
|
|
apmConfig.gain_controller1.analog_gain_controller.clipped_level_min = 0;
|
|
apmConfig.gain_controller1.analog_gain_controller.enable_agc2_level_estimator = false;
|
|
apmConfig.gain_controller1.analog_gain_controller.enable_digital_adaptive = false;
|
|
//GainController2
|
|
apmConfig.gain_controller2.enabled = false;
|
|
apmConfig.gain_controller2.fixed_digital.gain_db = 0.f;
|
|
apmConfig.gain_controller2.adaptive_digital.enabled = true;
|
|
apmConfig.gain_controller2.adaptive_digital.vad_probability_attack = 1.f;
|
|
apmConfig.gain_controller2.adaptive_digital.level_estimator = webrtc::AudioProcessing::Config::GainController2::kRms;
|
|
apmConfig.gain_controller2.adaptive_digital.level_estimator_adjacent_speech_frames_threshold = 1;
|
|
apmConfig.gain_controller2.adaptive_digital.use_saturation_protector = true;
|
|
apmConfig.gain_controller2.adaptive_digital.initial_saturation_margin_db = 20.f;
|
|
apmConfig.gain_controller2.adaptive_digital.extra_saturation_margin_db = 2.f;
|
|
apmConfig.gain_controller2.adaptive_digital.gain_applier_adjacent_speech_frames_threshold = 1;
|
|
apmConfig.gain_controller2.adaptive_digital.max_gain_change_db_per_second = 3.f;
|
|
apmConfig.gain_controller2.adaptive_digital.max_output_noise_level_dbfs = -50.f;
|
|
//ResidualEchoDetector
|
|
apmConfig.residual_echo_detector.enabled = false;
|
|
//LevelEstimation
|
|
apmConfig.level_estimation.enabled = false;
|
|
|
|
return apmConfig;
|
|
}
|
|
|
|
void capture_thread(CallContext *ctx);
|
|
|
|
int main()
|
|
{
|
|
//初始化日志系统
|
|
Logger::Instance().add(std::make_shared<ConsoleChannel> ());
|
|
Logger::Instance().add(std::make_shared<FileChannel>());
|
|
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
|
|
|
// webrtc初始化
|
|
webrtc::AudioProcessing *apm = webrtc::AudioProcessingBuilder().Create();
|
|
if (!apm) {
|
|
PrintI("create apm failed.\n");
|
|
return -1;
|
|
}
|
|
webrtc::AudioProcessing::Config config = webtcConfigInit();
|
|
apm->ApplyConfig(config);
|
|
apm->Initialize();
|
|
config.gain_controller1.enabled = false;
|
|
config.gain_controller1.analog_gain_controller.enabled = false;
|
|
config.noise_suppression.enabled = true;
|
|
config.noise_suppression.level = config.noise_suppression.kVeryHigh;
|
|
webrtc::AudioProcessing *apm_ns = webrtc::AudioProcessingBuilder().Create();
|
|
apm_ns->ApplyConfig(config);
|
|
apm_ns->Initialize();
|
|
webrtc::StreamConfig streamConfig;
|
|
streamConfig.set_has_keyboard(false);
|
|
streamConfig.set_num_channels(MIX_AUDIO_CHANNELS);
|
|
streamConfig.set_sample_rate_hz(MIX_AUDIO_RATE);
|
|
// agc module
|
|
LegacyAgc *agcInst = (LegacyAgc *)agc_init(MIX_AUDIO_RATE, kAgcModeAdaptiveDigital,
|
|
9,
|
|
3);
|
|
InfoL << "gain db: " << config.gain_controller1.compression_gain_db
|
|
<< ", target db:" << config.gain_controller1.target_level_dbfs;
|
|
|
|
// alsa设备参数
|
|
alsa::Config alsaConfig;
|
|
sprintf(alsaConfig.device, "default");
|
|
alsaConfig.period_time = MIX_AUDIO_SAMPLES * 1000000 / MIX_AUDIO_RATE;
|
|
alsaConfig.buffer_time = 5 * alsaConfig.period_time;
|
|
alsaConfig.channels = MIX_AUDIO_CHANNELS;
|
|
alsaConfig.format = SND_PCM_FORMAT_S16_LE;
|
|
alsaConfig.rate = MIX_AUDIO_RATE;
|
|
|
|
//
|
|
CallContext recordCtx;
|
|
recordCtx.mutex = new std::mutex;
|
|
recordCtx.list = new std::vector<audio_buf_t>();
|
|
recordCtx.apm_agc = apm;
|
|
recordCtx.apm_ns = apm_ns;
|
|
recordCtx.rtc_stream_config = &streamConfig;
|
|
recordCtx.alsa_config = &alsaConfig;
|
|
recordCtx.agcModule = agcInst;
|
|
|
|
char c;
|
|
bool quit = false;
|
|
while ((c = getchar()) != EOF && !quit)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'q': {
|
|
break;
|
|
}
|
|
case 's': {
|
|
recordCtx.running = true;
|
|
recordCtx.alsa_thread = new std::thread(capture_thread, &recordCtx);
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
void capture_thread(CallContext *ctx)
|
|
{
|
|
// 声卡初始化
|
|
alsa::AlsaDev usbCaptureDev;
|
|
if (usbCaptureDev.applyConfig(*ctx->alsa_config) < 0) {
|
|
PrintE("alsa config failed.\n");
|
|
return ;
|
|
}
|
|
if (usbCaptureDev.init(SND_PCM_STREAM_CAPTURE) < 0) {
|
|
PrintE("alsa init failed.\n");
|
|
return ;
|
|
}
|
|
PrintI("alsa init: %s\n", usbCaptureDev.configToString());
|
|
ctx->alsa = &usbCaptureDev;
|
|
|
|
uint8_t *capData = nullptr;
|
|
int buffer_size = usbCaptureDev.getFrames() * usbCaptureDev.getFrameSize();
|
|
capData = (uint8_t *)malloc(buffer_size);
|
|
assert(capData);
|
|
|
|
FILE *record_fp = fopen("/root/agc_record.pcm", "wb");
|
|
FILE *agc_fp = fopen("/root/agc_out.pcm", "wb");
|
|
FILE *scale_fp = fopen("/root/agc_scale.pcm", "wb");
|
|
InfoL << "-------------- start ----------------";
|
|
|
|
uint8_t *agcBuf = (uint8_t *)malloc(buffer_size);
|
|
while (ctx->running) {
|
|
// 采集
|
|
// t_capture = gettimeofday();
|
|
size_t read_size = usbCaptureDev.read(capData, buffer_size);
|
|
PrintI("alsa read %d\n", read_size);
|
|
if (read_size <= 0) {
|
|
msleep(1);
|
|
continue;
|
|
}
|
|
// alsa::vol_scaler_run((int16_t *)capData, buffer_size/2, 100);
|
|
|
|
fwrite(capData, 1, buffer_size, record_fp);
|
|
|
|
// {
|
|
// int samples = 160;
|
|
// int times = usbCaptureDev.getFrames() * 2 / samples;
|
|
// int16_t *in = (int16_t *)capData;
|
|
// int16_t *out = (int16_t *)agcBuf;
|
|
// for (int i = 0; i < times; ++i) {
|
|
// agc_process(ctx->agcModule, in + i*samples, out + i*samples, MIX_AUDIO_RATE, samples);
|
|
// }
|
|
// memcpy(capData, agcBuf, buffer_size);
|
|
// }
|
|
// 降噪
|
|
{
|
|
// std::unique_lock<std::mutex> lck(*ctx->mutex);
|
|
// t_process = gettimeofday();
|
|
ctx->apm_agc->ProcessStream((int16_t *)capData, *ctx->rtc_stream_config, *ctx->rtc_stream_config, (int16_t *)capData);
|
|
// ctx->apm_ns->ProcessStream((int16_t *)capData, *ctx->rtc_stream_config, *ctx->rtc_stream_config, (int16_t *)capData);
|
|
}
|
|
fwrite(capData, 1, buffer_size, agc_fp);
|
|
memset(capData, 0, buffer_size);
|
|
|
|
// alsa::vol_scaler_run((int16_t *)capData, buffer_size / 2, 100);
|
|
|
|
// fwrite(capData, 1, buffer_size, scale_fp);
|
|
}
|
|
usbCaptureDev.destory();
|
|
if (capData) free(capData);
|
|
ctx->alsa = nullptr;
|
|
} |