Line data Source code
1 : /*
2 : * Copyright (C) 2004-2026 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_ERROR("[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_ERROR("[webrtc-ap] set_stream_delay_ms failed");
150 : }
151 :
152 0 : if (apm->gain_control()->set_stream_analog_level(analogLevel_) != webrtcNoError) {
153 0 : JAMI_ERROR("[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_ERROR("[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
|