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
*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
)
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.");
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");
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");
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");
113 dgc
= agc
? FALSE
: 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");
120 pa_log("You must pick only one between analog and digital gain control");
124 mobile
= DEFAULT_MOBILE
;
125 if (pa_modargs_get_value_boolean(ma
, "mobile", &mobile
) < 0) {
126 pa_log("Failed to parse mobile value");
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");
137 if (ec
->params
.drift_compensation
) {
138 pa_log("Can't use drift_compensation in mobile mode");
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");
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");
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");
159 apm
= webrtc::AudioProcessing::Create(0);
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
;
167 apm
->set_sample_rate_hz(source_ss
->rate
);
169 apm
->set_num_channels(source_ss
->channels
, source_ss
->channels
);
170 apm
->set_num_reverse_channels(sink_ss
->channels
);
173 apm
->high_pass_filter()->Enable(true);
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);
180 apm
->echo_cancellation()->enable_drift_compensation(false);
183 apm
->echo_cancellation()->Enable(true);
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);
191 apm
->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh
);
192 apm
->noise_suppression()->Enable(true);
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 ec
->params
.priv
.webrtc
.agc
= FALSE
;
201 apm
->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital
);
202 ec
->params
.priv
.webrtc
.agc
= FALSE
;
204 apm
->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog
);
205 if (apm
->gain_control()->set_analog_level_limits(0, PA_VOLUME_NORM
-1) != apm
->kNoError
) {
206 pa_log("Failed to initialise AGC");
209 ec
->params
.priv
.webrtc
.agc
= TRUE
;
212 apm
->gain_control()->Enable(true);
215 apm
->voice_detection()->Enable(true);
217 ec
->params
.priv
.webrtc
.apm
= apm
;
218 ec
->params
.priv
.webrtc
.sample_spec
= *source_ss
;
219 ec
->params
.priv
.webrtc
.blocksize
= *blocksize
= (uint64_t)pa_bytes_per_second(source_ss
) * BLOCK_SIZE_US
/ PA_USEC_PER_SEC
;
228 webrtc::AudioProcessing::Destroy(apm
);
233 void pa_webrtc_ec_play(pa_echo_canceller
*ec
, const uint8_t *play
) {
234 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
235 webrtc::AudioFrame play_frame
;
236 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
238 play_frame
._audioChannel
= ss
->channels
;
239 play_frame
._frequencyInHz
= ss
->rate
;
240 play_frame
._payloadDataLengthInSamples
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
);
241 memcpy(play_frame
._payloadData
, play
, ec
->params
.priv
.webrtc
.blocksize
);
243 apm
->AnalyzeReverseStream(&play_frame
);
246 void pa_webrtc_ec_record(pa_echo_canceller
*ec
, const uint8_t *rec
, uint8_t *out
) {
247 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
248 webrtc::AudioFrame out_frame
;
249 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
252 out_frame
._audioChannel
= ss
->channels
;
253 out_frame
._frequencyInHz
= ss
->rate
;
254 out_frame
._payloadDataLengthInSamples
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
);
255 memcpy(out_frame
._payloadData
, rec
, ec
->params
.priv
.webrtc
.blocksize
);
257 if (ec
->params
.priv
.webrtc
.agc
) {
259 pa_echo_canceller_get_capture_volume(ec
, &v
);
260 apm
->gain_control()->set_stream_analog_level(pa_cvolume_avg(&v
));
263 apm
->set_stream_delay_ms(0);
264 apm
->ProcessStream(&out_frame
);
266 if (ec
->params
.priv
.webrtc
.agc
) {
267 pa_cvolume_set(&v
, ss
->channels
, apm
->gain_control()->stream_analog_level());
268 pa_echo_canceller_set_capture_volume(ec
, &v
);
271 memcpy(out
, out_frame
._payloadData
, ec
->params
.priv
.webrtc
.blocksize
);
274 void pa_webrtc_ec_set_drift(pa_echo_canceller
*ec
, float drift
) {
275 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
276 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
278 apm
->echo_cancellation()->set_stream_drift_samples(drift
* ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
));
281 void pa_webrtc_ec_run(pa_echo_canceller
*ec
, const uint8_t *rec
, const uint8_t *play
, uint8_t *out
) {
282 pa_webrtc_ec_play(ec
, play
);
283 pa_webrtc_ec_record(ec
, rec
, out
);
286 void pa_webrtc_ec_done(pa_echo_canceller
*ec
) {
287 if (ec
->params
.priv
.webrtc
.apm
) {
288 webrtc::AudioProcessing::Destroy((webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
);
289 ec
->params
.priv
.webrtc
.apm
= NULL
;