Line data Source code
1 : /* 2 : * Copyright (C) 2004-2024 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( 98 : webrtc::EchoCancellation::SuppressionLevel::kHighSuppression) 99 0 : != webrtcNoError) { 100 0 : JAMI_ERROR("[webrtc-ap] Error setting echo cancellation level"); 101 : } 102 0 : if (apm->echo_cancellation()->enable_drift_compensation(true) != webrtcNoError) { 103 0 : JAMI_ERROR("[webrtc-ap] Error enabling echo cancellation drift compensation"); 104 : } 105 0 : } 106 : 107 : void 108 0 : WebRTCAudioProcessor::enableVoiceActivityDetection(bool enabled) 109 : { 110 0 : JAMI_LOG("[webrtc-ap] enableVoiceActivityDetection {}", enabled); 111 0 : if (apm->voice_detection()->Enable(enabled) != webrtcNoError) { 112 0 : JAMI_ERROR("[webrtc-ap] Error enabling voice activation detection"); 113 : } 114 0 : if (apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::kVeryLowLikelihood) 115 0 : != webrtcNoError) { 116 0 : JAMI_ERROR("[webrtc-ap] Error setting voice detection likelihood"); 117 : } 118 : // asserted to be 10 in voice_detection_impl.cc 119 0 : if (apm->voice_detection()->set_frame_size_ms(10) != webrtcNoError) { 120 0 : JAMI_ERROR("[webrtc-ap] Error setting voice detection frame size"); 121 : } 122 0 : } 123 : 124 : std::shared_ptr<AudioFrame> 125 0 : WebRTCAudioProcessor::getProcessed() 126 : { 127 0 : if (tidyQueues()) { 128 0 : return {}; 129 : } 130 : 131 0 : int driftSamples = playbackQueue_.samples() - recordQueue_.samples(); 132 : 133 0 : auto playback = playbackQueue_.dequeue(); 134 0 : auto record = recordQueue_.dequeue(); 135 0 : if (!playback || !record) { 136 0 : return {}; 137 : } 138 0 : webrtc::StreamConfig sc((int) format_.sample_rate, (int) format_.nb_channels); 139 : 140 : // process reverse in place 141 0 : float** playData = (float**) playback->pointer()->extended_data; 142 0 : if (apm->ProcessReverseStream(playData, sc, sc, playData) != webrtcNoError) { 143 0 : JAMI_ERR("[webrtc-ap] ProcessReverseStream failed"); 144 : } 145 : 146 : // process deinterleaved float recorded data 147 : // TODO: maybe implement this to see if it's better than automatic drift compensation 148 : // (it MUST be called prior to ProcessStream) 149 : // delay = (t_render - t_analyze) + (t_process - t_capture) 150 0 : if (apm->set_stream_delay_ms(0) != webrtcNoError) { 151 0 : JAMI_ERR("[webrtc-ap] set_stream_delay_ms failed"); 152 : } 153 : 154 0 : if (apm->gain_control()->set_stream_analog_level(analogLevel_) != webrtcNoError) { 155 0 : JAMI_ERR("[webrtc-ap] set_stream_analog_level failed"); 156 : } 157 0 : apm->echo_cancellation()->set_stream_drift_samples(driftSamples); 158 : 159 : // process in place 160 0 : float** recData = (float**) record->pointer()->extended_data; 161 0 : if (apm->ProcessStream(recData, sc, sc, recData) != webrtcNoError) { 162 0 : JAMI_ERR("[webrtc-ap] ProcessStream failed"); 163 : } 164 : 165 0 : analogLevel_ = apm->gain_control()->stream_analog_level(); 166 0 : record->has_voice = apm->voice_detection()->is_enabled() 167 0 : && getStabilizedVoiceActivity(apm->voice_detection()->stream_has_voice()); 168 0 : return record; 169 0 : } 170 : 171 : } // namespace jami