echo-cancel: Fix webrtc gain control initialisation
[profile/ivi/pulseaudio-panda.git] / 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 FALSE
46 #define DEFAULT_DIGITAL_GAIN_CONTROL TRUE
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 *source_ss, pa_channel_map *source_map,
81                             pa_sample_spec *sink_ss, pa_channel_map *sink_map,
82                             uint32_t *blocksize, const char *args)
83 {
84     webrtc::AudioProcessing *apm = NULL;
85     pa_bool_t hpf, ns, agc, dgc, mobile, cn;
86     int rm;
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
95     hpf = DEFAULT_HIGH_PASS_FILTER;
96     if (pa_modargs_get_value_boolean(ma, "high_pass_filter", &hpf) < 0) {
97         pa_log("Failed to parse high_pass_filter value");
98         goto fail;
99     }
100
101     ns = DEFAULT_NOISE_SUPPRESSION;
102     if (pa_modargs_get_value_boolean(ma, "noise_suppression", &ns) < 0) {
103         pa_log("Failed to parse noise_suppression value");
104         goto fail;
105     }
106
107     agc = DEFAULT_ANALOG_GAIN_CONTROL;
108     if (pa_modargs_get_value_boolean(ma, "analog_gain_control", &agc) < 0) {
109         pa_log("Failed to parse analog_gain_control value");
110         goto fail;
111     }
112
113     dgc = DEFAULT_DIGITAL_GAIN_CONTROL;
114     if (pa_modargs_get_value_boolean(ma, "digital_gain_control", &dgc) < 0) {
115         pa_log("Failed to parse digital_gain_control value");
116         goto fail;
117     }
118
119     if (agc && dgc) {
120         pa_log("You must pick only one between analog and digital gain control");
121         goto fail;
122     }
123
124     mobile = DEFAULT_MOBILE;
125     if (pa_modargs_get_value_boolean(ma, "mobile", &mobile) < 0) {
126         pa_log("Failed to parse mobile value");
127         goto fail;
128     }
129
130     ec->params.drift_compensation = DEFAULT_DRIFT_COMPENSATION;
131     if (pa_modargs_get_value_boolean(ma, "drift_compensation", &ec->params.drift_compensation) < 0) {
132         pa_log("Failed to parse drift_compensation value");
133         goto fail;
134     }
135
136     if (mobile) {
137         if (ec->params.drift_compensation) {
138             pa_log("Can't use drift_compensation in mobile mode");
139             goto fail;
140         }
141
142         if ((rm = routing_mode_from_string(pa_modargs_get_value(ma, "routing_mode", DEFAULT_ROUTING_MODE))) < 0) {
143             pa_log("Failed to parse routing_mode value");
144             goto fail;
145         }
146
147         cn = DEFAULT_COMFORT_NOISE;
148         if (pa_modargs_get_value_boolean(ma, "comfort_noise", &cn) < 0) {
149             pa_log("Failed to parse cn value");
150             goto fail;
151         }
152     } else {
153         if (pa_modargs_get_value(ma, "comfort_noise", NULL) || pa_modargs_get_value(ma, "routing_mode", NULL)) {
154             pa_log("The routing_mode and comfort_noise options are only valid with mobile=true");
155             goto fail;
156         }
157     }
158
159     apm = webrtc::AudioProcessing::Create(0);
160
161     source_ss->format = PA_SAMPLE_S16NE;
162     *sink_ss = *source_ss;
163     /* FIXME: the implementation actually allows a different number of
164      * source/sink channels. Do we want to support that? */
165     *sink_map = *source_map;
166
167     apm->set_sample_rate_hz(source_ss->rate);
168
169     apm->set_num_channels(source_ss->channels, source_ss->channels);
170     apm->set_num_reverse_channels(sink_ss->channels);
171
172     if (hpf)
173         apm->high_pass_filter()->Enable(true);
174
175     if (!mobile) {
176         if (ec->params.drift_compensation) {
177             apm->echo_cancellation()->set_device_sample_rate_hz(source_ss->rate);
178             apm->echo_cancellation()->enable_drift_compensation(true);
179         } else {
180             apm->echo_cancellation()->enable_drift_compensation(false);
181         }
182
183         apm->echo_cancellation()->Enable(true);
184     } else {
185         apm->echo_control_mobile()->set_routing_mode(static_cast<webrtc::EchoControlMobile::RoutingMode>(rm));
186         apm->echo_control_mobile()->enable_comfort_noise(cn);
187         apm->echo_control_mobile()->Enable(true);
188     }
189
190     if (ns) {
191         apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh);
192         apm->noise_suppression()->Enable(true);
193     }
194
195     if (agc || dgc) {
196         if (mobile && rm <= webrtc::EchoControlMobile::kEarpiece)
197             /* Maybe this should be a knob, but we've got a lot of knobs already */
198             apm->gain_control()->set_mode(webrtc::GainControl::kFixedDigital);
199         else if (dgc)
200             apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital);
201         else {
202             /* FIXME: Hook up for analog AGC */
203             pa_log("Analog gain control isn't implemented yet -- using ditital gain control.");
204             apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital);
205         }
206
207         apm->gain_control()->Enable(true);
208     }
209
210     apm->voice_detection()->Enable(true);
211
212     ec->params.priv.webrtc.apm = apm;
213     ec->params.priv.webrtc.sample_spec = *source_ss;
214     ec->params.priv.webrtc.blocksize = *blocksize = (uint64_t)pa_bytes_per_second(source_ss) * BLOCK_SIZE_US / PA_USEC_PER_SEC;
215
216     pa_modargs_free(ma);
217     return TRUE;
218
219 fail:
220     if (ma)
221         pa_modargs_free(ma);
222     if (apm)
223         webrtc::AudioProcessing::Destroy(apm);
224
225     return FALSE;
226 }
227
228 void pa_webrtc_ec_play(pa_echo_canceller *ec, const uint8_t *play) {
229     webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.priv.webrtc.apm;
230     webrtc::AudioFrame play_frame;
231     const pa_sample_spec *ss = &ec->params.priv.webrtc.sample_spec;
232
233     play_frame._audioChannel = ss->channels;
234     play_frame._frequencyInHz = ss->rate;
235     play_frame._payloadDataLengthInSamples = ec->params.priv.webrtc.blocksize / pa_frame_size(ss);
236     memcpy(play_frame._payloadData, play, ec->params.priv.webrtc.blocksize);
237
238     apm->AnalyzeReverseStream(&play_frame);
239 }
240
241 void pa_webrtc_ec_record(pa_echo_canceller *ec, const uint8_t *rec, uint8_t *out) {
242     webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.priv.webrtc.apm;
243     webrtc::AudioFrame out_frame;
244     const pa_sample_spec *ss = &ec->params.priv.webrtc.sample_spec;
245
246     out_frame._audioChannel = ss->channels;
247     out_frame._frequencyInHz = ss->rate;
248     out_frame._payloadDataLengthInSamples = ec->params.priv.webrtc.blocksize / pa_frame_size(ss);
249     memcpy(out_frame._payloadData, rec, ec->params.priv.webrtc.blocksize);
250
251     apm->set_stream_delay_ms(0);
252     apm->ProcessStream(&out_frame);
253
254     memcpy(out, out_frame._payloadData, ec->params.priv.webrtc.blocksize);
255 }
256
257 void pa_webrtc_ec_set_drift(pa_echo_canceller *ec, float drift) {
258     webrtc::AudioProcessing *apm = (webrtc::AudioProcessing*)ec->params.priv.webrtc.apm;
259     const pa_sample_spec *ss = &ec->params.priv.webrtc.sample_spec;
260
261     apm->echo_cancellation()->set_stream_drift_samples(drift * ec->params.priv.webrtc.blocksize / pa_frame_size(ss));
262 }
263
264 void pa_webrtc_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
265     pa_webrtc_ec_play(ec, play);
266     pa_webrtc_ec_record(ec, rec, out);
267 }
268
269 void pa_webrtc_ec_done(pa_echo_canceller *ec) {
270     if (ec->params.priv.webrtc.apm) {
271         webrtc::AudioProcessing::Destroy((webrtc::AudioProcessing*)ec->params.priv.webrtc.apm);
272         ec->params.priv.webrtc.apm = NULL;
273     }
274 }