#include "BoostLog.h" #include "SpeexDsp.h" #include "Utility.h" #include "WebRtcAecm.h" #include "api/audio/echo_canceller3_config.h" #include "api/audio/echo_canceller3_factory.h" #include "main.h" #include "modules/audio_processing/aec3/echo_canceller3.h" #include class EchoRecordPrivate { public: EchoRecordPrivate() { std::unique_ptr factory = std::make_unique(); echoCanceller = factory->Create(16000, 1, 1); // nearendBuffer = std::make_unique(16000, 1, 16000, 1, 16000, 1); // farendBuffer = std::make_unique(16000, 1, 16000, 1, 16000, 1); } std::unique_ptr echoCanceller; // std::unique_ptr nearendBuffer; // std::unique_ptr farendBuffer; }; EchoRecordTask::EchoRecordTask() : m_d{new EchoRecordPrivate()} { } EchoRecordTask::~EchoRecordTask() { if (m_d != nullptr) { delete m_d; } } void EchoRecordTask::setDsp(Dsp dsp) { if (m_dsp != dsp) { m_dsp = dsp; } } void EchoRecordTask::setChannels(int channels) { if (m_channels != channels) { m_channels = channels; } } // underrun occurred pcm播放饥饿 // 回采信号提前于mic信号,时间差<80ms // ./Record --echo --vqe=false --channels=2 // ./Record --echo --vqe=true --channels=2 // ./Record --echo --vqe=false --channels=1 void EchoRecordTask::run() { LOG(info) << "dsp use: " << dspToString(m_dsp); RkAudio::Format format; format.channels = m_channels; format.period = 20; m_speex = std::make_shared(); m_speex->start(format.sampleRate, m_channels, format.period); m_farendBuffer.resize(m_channels * sizeof(int16_t) * format.sampleRate / 1000 * format.period); m_nearendBuffer.resize(m_channels * sizeof(int16_t) * format.sampleRate / 1000 * format.period); m_webRtcAecm = std::make_shared(); m_webRtcAecm->start(format.sampleRate, format.channels, format.period); m_output = std::make_shared(); if (!m_output->open(sizeof(uint16_t), format.sampleRate, 2, format.period, m_dsp == Vqe)) { LOG(error) << "audio output open failed."; return; } m_outBuffer.resize(m_channels * sizeof(int16_t) * format.sampleRate / 1000 * format.period); m_input = std::make_shared(); m_input->setDataCallback([this, format](const RkAudio::Frame &frame) { memcpy(m_nearendBuffer.data(), frame.data, frame.byteSize); if (m_dsp == Speex) { m_speex->echoPlayback(reinterpret_cast(m_farendBuffer.data())); m_speex->echoCapture(reinterpret_cast(frame.data), reinterpret_cast(m_outBuffer.data())); } else if (m_dsp == AecMobile) { m_webRtcAecm->echoPlayback(reinterpret_cast(m_farendBuffer.data()), m_farendBuffer.size() / 2); m_webRtcAecm->echoCancellation(reinterpret_cast(frame.data), reinterpret_cast(m_nearendBuffer.data()), reinterpret_cast(m_outBuffer.data()), frame.frameSize); } else if (m_dsp == Aec3) { webrtc::StreamConfig config(format.sampleRate, format.channels); // 单声道 webrtc::AudioBuffer nearendBuffer(format.sampleRate, 1, format.sampleRate, 1, format.sampleRate, 1); webrtc::AudioBuffer farendBuffer(format.sampleRate, 1, format.sampleRate, 1, format.sampleRate, 1); webrtc::AudioBuffer linearOutputBuffer(format.sampleRate, 1, format.sampleRate, 1, format.sampleRate, 1); nearendBuffer.CopyFrom(reinterpret_cast(frame.data), config); farendBuffer.CopyFrom(reinterpret_cast(m_farendBuffer.data()), config); m_d->echoCanceller->AnalyzeRender(&farendBuffer); m_d->echoCanceller->AnalyzeCapture(&nearendBuffer); m_d->echoCanceller->ProcessCapture(&nearendBuffer, &linearOutputBuffer, /*level_change=*/false); linearOutputBuffer.CopyTo(config, reinterpret_cast(m_outBuffer.data())); } if (m_channels == 2) { m_output->write(frame.data, frame.byteSize); } else if (m_channels == 1) { auto filledData = duplicate(m_outBuffer.data(), m_outBuffer.size()); m_output->write(filledData.data(), filledData.size()); } memcpy(m_farendBuffer.data(), m_outBuffer.data(), m_outBuffer.size()); // m_output->write(reinterpret_cast(m_buffer.data()), m_buffer.size()); }); m_input->open(format, m_dsp == Vqe); }