2 This file is part of PulseAudio.
4 Copyright 2011 Collabora Ltd.
6 Contributor: Arun Raghavan <arun.raghavan@collabora.co.uk>
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.
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.
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
28 #include <pulse/cdecl.h>
31 #include <pulsecore/core-util.h>
32 #include <pulsecore/modargs.h>
34 #include <pulse/timeval.h>
35 #include "echo-cancel.h"
38 #include <audio_processing.h>
39 #include <module_common_types.h>
41 #define BLOCK_SIZE_US 10000
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
52 static const char* const valid_modargs
[] = {
55 "analog_gain_control",
56 "digital_gain_control",
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
;
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
;
89 if (!(ma
= pa_modargs_new(args
, valid_modargs
))) {
90 pa_log("Failed to parse submodule arguments.");
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");
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");
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");
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");
119 pa_log("You must pick only one between analog and digital gain control");
123 mobile
= DEFAULT_MOBILE
;
124 if (pa_modargs_get_value_boolean(ma
, "mobile", &mobile
) < 0) {
125 pa_log("Failed to parse mobile value");
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");
136 if (ec
->params
.drift_compensation
) {
137 pa_log("Can't use drift_compensation in mobile mode");
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");
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");
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");
158 apm
= webrtc::AudioProcessing::Create(0);
160 out_ss
->format
= PA_SAMPLE_S16NE
;
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
;
168 apm
->set_sample_rate_hz(out_ss
->rate
);
170 apm
->set_num_channels(out_ss
->channels
, out_ss
->channels
);
171 apm
->set_num_reverse_channels(play_ss
->channels
);
174 apm
->high_pass_filter()->Enable(true);
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);
181 apm
->echo_cancellation()->enable_drift_compensation(false);
184 apm
->echo_cancellation()->Enable(true);
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);
192 apm
->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh
);
193 apm
->noise_suppression()->Enable(true);
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
;
202 apm
->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital
);
203 ec
->params
.priv
.webrtc
.agc
= FALSE
;
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");
210 ec
->params
.priv
.webrtc
.agc
= TRUE
;
213 apm
->gain_control()->Enable(true);
216 apm
->voice_detection()->Enable(true);
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
);
230 webrtc::AudioProcessing::Destroy(apm
);
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
;
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
);
245 apm
->AnalyzeReverseStream(&play_frame
);
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
;
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
);
259 if (ec
->params
.priv
.webrtc
.agc
) {
261 pa_echo_canceller_get_capture_volume(ec
, &v
);
262 apm
->gain_control()->set_stream_analog_level(pa_cvolume_avg(&v
));
265 apm
->set_stream_delay_ms(0);
266 apm
->ProcessStream(&out_frame
);
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
);
273 memcpy(out
, out_frame
._payloadData
, ec
->params
.priv
.webrtc
.blocksize
);
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
;
280 apm
->echo_cancellation()->set_stream_drift_samples(drift
* ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
));
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
);
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
;