]> code.delx.au - pulseaudio/blob - src/modules/echo-cancel/webrtc.cc
Whitespace cleanup: Remove all multiple newlines
[pulseaudio] / src / modules / echo-cancel / webrtc.cc
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2011 Collabora Ltd.
5
6 Contributor: Arun Raghavan <arun.raghavan@collabora.co.uk>
7
8 PulseAudio is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published
10 by the Free Software Foundation; either version 2.1 of the License,
11 or (at your option) any later version.
12
13 PulseAudio is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with PulseAudio; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 USA.
22 ***/
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <pulse/cdecl.h>
29
30 PA_C_DECL_BEGIN
31 #include <pulsecore/core-util.h>
32 #include <pulsecore/modargs.h>
33
34 #include <pulse/timeval.h>
35 #include "echo-cancel.h"
36 PA_C_DECL_END
37
38 #include <audio_processing.h>
39 #include <module_common_types.h>
40
41 #define BLOCK_SIZE_US 10000
42
43 #define DEFAULT_HIGH_PASS_FILTER TRUE
44 #define DEFAULT_NOISE_SUPPRESSION TRUE
45 #define DEFAULT_ANALOG_GAIN_CONTROL TRUE
46 #define DEFAULT_DIGITAL_GAIN_CONTROL FALSE
47 #define DEFAULT_MOBILE FALSE
48 #define DEFAULT_ROUTING_MODE "speakerphone"
49 #define DEFAULT_COMFORT_NOISE TRUE
50 #define DEFAULT_DRIFT_COMPENSATION FALSE
51
52 static const char* const valid_modargs[] = {
53 "high_pass_filter",
54 "noise_suppression",
55 "analog_gain_control",
56 "digital_gain_control",
57 "mobile",
58 "routing_mode",
59 "comfort_noise",
60 "drift_compensation",
61 NULL
62 };
63
64 static int routing_mode_from_string(const char *rmode) {
65 if (pa_streq(rmode, "quiet-earpiece-or-headset"))
66 return webrtc::EchoControlMobile::kQuietEarpieceOrHeadset;
67 else if (pa_streq(rmode, "earpiece"))
68 return webrtc::EchoControlMobile::kEarpiece;
69 else if (pa_streq(rmode, "loud-earpiece"))
70 return webrtc::EchoControlMobile::kLoudEarpiece;
71 else if (pa_streq(rmode, "speakerphone"))
72 return webrtc::EchoControlMobile::kSpeakerphone;
73 else if (pa_streq(rmode, "loud-speakerphone"))
74 return webrtc::EchoControlMobile::kLoudSpeakerphone;
75 else
76 return -1;
77 }
78
79 pa_bool_t pa_webrtc_ec_init(pa_core *c, pa_echo_canceller *ec,
80 pa_sample_spec *rec_ss, pa_channel_map *rec_map,
81 pa_sample_spec *play_ss, pa_channel_map *play_map,
82 pa_sample_spec *out_ss, pa_channel_map *out_map,
83 uint32_t *nframes, const char *args) {
84 webrtc::AudioProcessing *apm = NULL;
85 pa_bool_t hpf, ns, agc, dgc, mobile, cn;
86 int rm = -1;
87 pa_modargs *ma;
88
89 if (!(ma = pa_modargs_new(args, valid_modargs))) {
90 pa_log("Failed to parse submodule arguments.");
91 goto fail;
92 }
93
94 hpf = DEFAULT_HIGH_PASS_FILTER;
95 if (pa_modargs_get_value_boolean(ma, "high_pass_filter", &hpf) < 0) {
96 pa_log("Failed to parse high_pass_filter value");
97 goto fail;
98 }
99
100 ns = DEFAULT_NOISE_SUPPRESSION;
101 if (pa_modargs_get_value_boolean(ma, "noise_suppression", &ns) < 0) {
102 pa_log("Failed to parse noise_suppression value");
103 goto fail;
104 }
105
106 agc = DEFAULT_ANALOG_GAIN_CONTROL;
107 if (pa_modargs_get_value_boolean(ma, "analog_gain_control", &agc) < 0) {
108 pa_log("Failed to parse analog_gain_control value");
109 goto fail;
110 }
111
112 dgc = agc ? FALSE : DEFAULT_DIGITAL_GAIN_CONTROL;
113 if (pa_modargs_get_value_boolean(ma, "digital_gain_control", &dgc) < 0) {
114 pa_log("Failed to parse digital_gain_control value");
115 goto fail;
116 }
117
118 if (agc && dgc) {
119 pa_log("You must pick only one between analog and digital gain control");
120 goto fail;
121 }
122
123 mobile = DEFAULT_MOBILE;
124 if (pa_modargs_get_value_boolean(ma, "mobile", &mobile) < 0) {
125 pa_log("Failed to parse mobile value");
126 goto fail;
127 }
128
129 ec->params.drift_compensation = DEFAULT_DRIFT_COMPENSATION;
130 if (pa_modargs_get_value_boolean(ma, "drift_compensation", &ec->params.drift_compensation) < 0) {
131 pa_log("Failed to parse drift_compensation value");
132 goto fail;
133 }
134
135 if (mobile) {
136 if (ec->params.drift_compensation) {
137 pa_log("Can't use drift_compensation in mobile mode");
138 goto fail;
139 }
140
141 if ((rm = routing_mode_from_string(pa_modargs_get_value(ma, "routing_mode", DEFAULT_ROUTING_MODE))) < 0) {
142 pa_log("Failed to parse routing_mode value");
143 goto fail;
144 }
145
146 cn = DEFAULT_COMFORT_NOISE;
147 if (pa_modargs_get_value_boolean(ma, "comfort_noise", &cn) < 0) {
148 pa_log("Failed to parse cn value");
149 goto fail;
150 }
151 } else {
152 if (pa_modargs_get_value(ma, "comfort_noise", NULL) || pa_modargs_get_value(ma, "routing_mode", NULL)) {
153 pa_log("The routing_mode and comfort_noise options are only valid with mobile=true");
154 goto fail;
155 }
156 }
157
158 apm = webrtc::AudioProcessing::Create(0);
159
160 out_ss->format = PA_SAMPLE_S16NE;
161 *play_ss = *out_ss;
162 /* FIXME: the implementation actually allows a different number of
163 * source/sink channels. Do we want to support that? */
164 *play_map = *out_map;
165 *rec_ss = *out_ss;
166 *rec_map = *out_map;
167
168 apm->set_sample_rate_hz(out_ss->rate);
169
170 apm->set_num_channels(out_ss->channels, out_ss->channels);
171 apm->set_num_reverse_channels(play_ss->channels);
172
173 if (hpf)
174 apm->high_pass_filter()->Enable(true);
175
176 if (!mobile) {
177 if (ec->params.drift_compensation) {
178 apm->echo_cancellation()->set_device_sample_rate_hz(out_ss->rate);
179 apm->echo_cancellation()->enable_drift_compensation(true);
180 } else {
181 apm->echo_cancellation()->enable_drift_compensation(false);
182 }
183
184 apm->echo_cancellation()->Enable(true);
185 } else {
186 apm->echo_control_mobile()->set_routing_mode(static_cast<webrtc::EchoControlMobile::RoutingMode>(rm));
187 apm->echo_control_mobile()->enable_comfort_noise(cn);
188 apm->echo_control_mobile()->Enable(true);
189 }
190
191 if (ns) {
192 apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh);
193 apm->noise_suppression()->Enable(true);
194 }
195
196 if (agc || dgc) {
197 if (mobile && rm <= webrtc::EchoControlMobile::kEarpiece) {
198 /* Maybe this should be a knob, but we've got a lot of knobs already */
199 apm->gain_control()->set_mode(webrtc::GainControl::kFixedDigital);
200 ec->params.priv.webrtc.agc = FALSE;
201 } else if (dgc) {
202 apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital);
203 ec->params.priv.webrtc.agc = FALSE;
204 } else {
205 apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog);
206 if (apm->gain_control()->set_analog_level_limits(0, PA_VOLUME_NORM-1) != apm->kNoError) {
207 pa_log("Failed to initialise AGC");
208 goto fail;
209 }
210 ec->params.priv.webrtc.agc = TRUE;
211 }
212
213 apm->gain_control()->Enable(true);
214 }
215
216 apm->voice_detection()->Enable(true);
217
218 ec->params.priv.webrtc.apm = apm;
219 ec->params.priv.webrtc.sample_spec = *out_ss;
220 ec->params.priv.webrtc.blocksize = (uint64_t)pa_bytes_per_second(out_ss) * BLOCK_SIZE_US / PA_USEC_PER_SEC;
221 *nframes = ec->params.priv.webrtc.blocksize / pa_frame_size(out_ss);
222
223 pa_modargs_free(ma);
224 return TRUE;
225
226 fail:
227 if (ma)
228 pa_modargs_free(ma);
229 if (apm)
230 webrtc::AudioProcessing::Destroy(apm);
231
232 return FALSE;
233 }
234
235 void pa_webrtc_ec_play(pa_echo_canceller *ec, const uint8_t *play) {
236 webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.priv.webrtc.apm;
237 webrtc::AudioFrame play_frame;
238 const pa_sample_spec *ss = &ec->params.priv.webrtc.sample_spec;
239
240 play_frame._audioChannel = ss->channels;
241 play_frame._frequencyInHz = ss->rate;
242 play_frame._payloadDataLengthInSamples = ec->params.priv.webrtc.blocksize / pa_frame_size(ss);
243 memcpy(play_frame._payloadData, play, ec->params.priv.webrtc.blocksize);
244
245 apm->AnalyzeReverseStream(&play_frame);
246 }
247
248 void pa_webrtc_ec_record(pa_echo_canceller *ec, const uint8_t *rec, uint8_t *out) {
249 webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.priv.webrtc.apm;
250 webrtc::AudioFrame out_frame;
251 const pa_sample_spec *ss = &ec->params.priv.webrtc.sample_spec;
252 pa_cvolume v;
253
254 out_frame._audioChannel = ss->channels;
255 out_frame._frequencyInHz = ss->rate;
256 out_frame._payloadDataLengthInSamples = ec->params.priv.webrtc.blocksize / pa_frame_size(ss);
257 memcpy(out_frame._payloadData, rec, ec->params.priv.webrtc.blocksize);
258
259 if (ec->params.priv.webrtc.agc) {
260 pa_cvolume_init(&v);
261 pa_echo_canceller_get_capture_volume(ec, &v);
262 apm->gain_control()->set_stream_analog_level(pa_cvolume_avg(&v));
263 }
264
265 apm->set_stream_delay_ms(0);
266 apm->ProcessStream(&out_frame);
267
268 if (ec->params.priv.webrtc.agc) {
269 pa_cvolume_set(&v, ss->channels, apm->gain_control()->stream_analog_level());
270 pa_echo_canceller_set_capture_volume(ec, &v);
271 }
272
273 memcpy(out, out_frame._payloadData, ec->params.priv.webrtc.blocksize);
274 }
275
276 void pa_webrtc_ec_set_drift(pa_echo_canceller *ec, float drift) {
277 webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.priv.webrtc.apm;
278 const pa_sample_spec *ss = &ec->params.priv.webrtc.sample_spec;
279
280 apm->echo_cancellation()->set_stream_drift_samples(drift * ec->params.priv.webrtc.blocksize / pa_frame_size(ss));
281 }
282
283 void pa_webrtc_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
284 pa_webrtc_ec_play(ec, play);
285 pa_webrtc_ec_record(ec, rec, out);
286 }
287
288 void pa_webrtc_ec_done(pa_echo_canceller *ec) {
289 if (ec->params.priv.webrtc.apm) {
290 webrtc::AudioProcessing::Destroy((webrtc::AudioProcessing*)ec->params.priv.webrtc.apm);
291 ec->params.priv.webrtc.apm = NULL;
292 }
293 }