Line data Source code
1 : /* 2 : * Copyright (C) 2004-2025 Savoir-faire Linux Inc. 3 : * 4 : * This program is free software: you can redistribute it and/or modify 5 : * it under the terms of the GNU General Public License as published by 6 : * the Free Software Foundation, either version 3 of the License, or 7 : * (at your option) any later version. 8 : * 9 : * This program is distributed in the hope that it will be useful, 10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 : * GNU General Public License for more details. 13 : * 14 : * You should have received a copy of the GNU General Public License 15 : * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 : */ 17 : 18 : #include "webrtc.h" 19 : #include "logger.h" 20 : 21 : #include <webrtc/modules/audio_processing/include/audio_processing.h> 22 : 23 : namespace jami { 24 : 25 : inline size_t 26 0 : webrtcFrameSize(AudioFormat format) 27 : { 28 0 : return (size_t) (webrtc::AudioProcessing::kChunkSizeMs * format.sample_rate / 1000); 29 : } 30 : 31 : constexpr int webrtcNoError = webrtc::AudioProcessing::kNoError; 32 : 33 0 : WebRTCAudioProcessor::WebRTCAudioProcessor(AudioFormat format, unsigned /* frameSize */) 34 0 : : AudioProcessor(format.withSampleFormat(AV_SAMPLE_FMT_FLTP), webrtcFrameSize(format)) 35 : { 36 0 : JAMI_LOG("[webrtc-ap] WebRTCAudioProcessor, frame size = {:d} (={:d} ms), channels = {:d}", 37 : frameSize_, 38 : frameDurationMs_, 39 : format_.nb_channels); 40 0 : webrtc::Config config; 41 0 : config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(true)); 42 0 : config.Set<webrtc::DelayAgnostic>(new webrtc::DelayAgnostic(true)); 43 : 44 0 : apm.reset(webrtc::AudioProcessing::Create(config)); 45 : 46 0 : webrtc::StreamConfig streamConfig((int) format_.sample_rate, (int) format_.nb_channels); 47 : webrtc::ProcessingConfig pconfig = { 48 : streamConfig, /* input stream */ 49 : streamConfig, /* output stream */ 50 : streamConfig, /* reverse input stream */ 51 : streamConfig, /* reverse output stream */ 52 0 : }; 53 : 54 0 : if (apm->Initialize(pconfig) != webrtcNoError) { 55 0 : JAMI_ERROR("[webrtc-ap] Error initialising audio processing module"); 56 : } 57 0 : } 58 : 59 : void 60 0 : WebRTCAudioProcessor::enableNoiseSuppression(bool enabled) 61 : { 62 0 : JAMI_LOG("[webrtc-ap] enableNoiseSuppression {}", enabled); 63 0 : if (apm->noise_suppression()->Enable(enabled) != webrtcNoError) { 64 0 : JAMI_ERROR("[webrtc-ap] Error enabling noise suppression"); 65 : } 66 0 : if (apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kVeryHigh) != webrtcNoError) { 67 0 : JAMI_ERROR("[webrtc-ap] Error setting noise suppression level"); 68 : } 69 0 : if (apm->high_pass_filter()->Enable(enabled) != webrtcNoError) { 70 0 : JAMI_ERROR("[webrtc-ap] Error enabling high pass filter"); 71 : } 72 0 : } 73 : 74 : void 75 0 : WebRTCAudioProcessor::enableAutomaticGainControl(bool enabled) 76 : { 77 0 : JAMI_LOG("[webrtc-ap] enableAutomaticGainControl {}", enabled); 78 0 : if (apm->gain_control()->Enable(enabled) != webrtcNoError) { 79 0 : JAMI_ERROR("[webrtc-ap] Error enabling automatic gain control"); 80 : } 81 0 : if (apm->gain_control()->set_analog_level_limits(0, 255) != webrtcNoError) { 82 0 : JAMI_ERROR("[webrtc-ap] Error setting automatic gain control analog level limits"); 83 : } 84 0 : if (apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog) != webrtcNoError) { 85 0 : JAMI_ERROR("[webrtc-ap] Error setting automatic gain control mode"); 86 : } 87 0 : } 88 : 89 : void 90 0 : WebRTCAudioProcessor::enableEchoCancel(bool enabled) 91 : { 92 0 : JAMI_LOG("[webrtc-ap] enableEchoCancel {}", enabled); 93 : 94 0 : if (apm->echo_cancellation()->Enable(enabled) != webrtcNoError) { 95 0 : JAMI_ERROR("[webrtc-ap] Error enabling echo cancellation"); 96 : } 97 0 : if (apm->echo_cancellation()->set_suppression_level(webrtc::EchoCancellation::SuppressionLevel::kHighSuppression) 98 0 : != webrtcNoError) { 99 0 : JAMI_ERROR("[webrtc-ap] Error setting echo cancellation level"); 100 : } 101 0 : if (apm->echo_cancellation()->enable_drift_compensation(true) != webrtcNoError) { 102 0 : JAMI_ERROR("[webrtc-ap] Error enabling echo cancellation drift compensation"); 103 : } 104 0 : } 105 : 106 : void 107 0 : WebRTCAudioProcessor::enableVoiceActivityDetection(bool enabled) 108 : { 109 0 : JAMI_LOG("[webrtc-ap] enableVoiceActivityDetection {}", enabled); 110 0 : if (apm->voice_detection()->Enable(enabled) != webrtcNoError) { 111 0 : JAMI_ERROR("[webrtc-ap] Error enabling voice activation detection"); 112 : } 113 0 : if (apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::kVeryLowLikelihood) != webrtcNoError) { 114 0 : JAMI_ERROR("[webrtc-ap] Error setting voice detection likelihood"); 115 : } 116 : // asserted to be 10 in voice_detection_impl.cc 117 0 : if (apm->voice_detection()->set_frame_size_ms(10) != webrtcNoError) { 118 0 : JAMI_ERROR("[webrtc-ap] Error setting voice detection frame size"); 119 : } 120 0 : } 121 : 122 : std::shared_ptr<AudioFrame> 123 0 : WebRTCAudioProcessor::getProcessed() 124 : { 125 0 : if (tidyQueues()) { 126 0 : return {}; 127 : } 128 : 129 0 : int driftSamples = playbackQueue_.samples() - recordQueue_.samples(); 130 : 131 0 : auto playback = playbackQueue_.dequeue(); 132 0 : auto record = recordQueue_.dequeue(); 133 0 : if (!playback || !record) { 134 0 : return {}; 135 : } 136 0 : webrtc::StreamConfig sc((int) format_.sample_rate, (int) format_.nb_channels); 137 : 138 : // process reverse in place 139 0 : float** playData = (float**) playback->pointer()->extended_data; 140 0 : if (apm->ProcessReverseStream(playData, sc, sc, playData) != webrtcNoError) { 141 0 : JAMI_ERR("[webrtc-ap] ProcessReverseStream failed"); 142 : } 143 : 144 : // process deinterleaved float recorded data 145 : // TODO: maybe implement this to see if it's better than automatic drift compensation 146 : // (it MUST be called prior to ProcessStream) 147 : // delay = (t_render - t_analyze) + (t_process - t_capture) 148 0 : if (apm->set_stream_delay_ms(0) != webrtcNoError) { 149 0 : JAMI_ERR("[webrtc-ap] set_stream_delay_ms failed"); 150 : } 151 : 152 0 : if (apm->gain_control()->set_stream_analog_level(analogLevel_) != webrtcNoError) { 153 0 : JAMI_ERR("[webrtc-ap] set_stream_analog_level failed"); 154 : } 155 0 : apm->echo_cancellation()->set_stream_drift_samples(driftSamples); 156 : 157 : // process in place 158 0 : float** recData = (float**) record->pointer()->extended_data; 159 0 : if (apm->ProcessStream(recData, sc, sc, recData) != webrtcNoError) { 160 0 : JAMI_ERR("[webrtc-ap] ProcessStream failed"); 161 : } 162 : 163 0 : analogLevel_ = apm->gain_control()->stream_analog_level(); 164 0 : record->has_voice = apm->voice_detection()->is_enabled() 165 0 : && getStabilizedVoiceActivity(apm->voice_detection()->stream_has_voice()); 166 0 : return record; 167 0 : } 168 : 169 : } // namespace jami