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 *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.");
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
= (uint64_t)pa_bytes_per_second(source_ss
) * BLOCK_SIZE_US
/ PA_USEC_PER_SEC
;
220 *nframes
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(source_ss
);
229 webrtc::AudioProcessing::Destroy(apm
);
234 void pa_webrtc_ec_play(pa_echo_canceller
*ec
, const uint8_t *play
) {
235 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
236 webrtc::AudioFrame play_frame
;
237 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
239 play_frame
._audioChannel
= ss
->channels
;
240 play_frame
._frequencyInHz
= ss
->rate
;
241 play_frame
._payloadDataLengthInSamples
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
);
242 memcpy(play_frame
._payloadData
, play
, ec
->params
.priv
.webrtc
.blocksize
);
244 apm
->AnalyzeReverseStream(&play_frame
);
247 void pa_webrtc_ec_record(pa_echo_canceller
*ec
, const uint8_t *rec
, uint8_t *out
) {
248 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
249 webrtc::AudioFrame out_frame
;
250 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
253 out_frame
._audioChannel
= ss
->channels
;
254 out_frame
._frequencyInHz
= ss
->rate
;
255 out_frame
._payloadDataLengthInSamples
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
);
256 memcpy(out_frame
._payloadData
, rec
, ec
->params
.priv
.webrtc
.blocksize
);
258 if (ec
->params
.priv
.webrtc
.agc
) {
260 pa_echo_canceller_get_capture_volume(ec
, &v
);
261 apm
->gain_control()->set_stream_analog_level(pa_cvolume_avg(&v
));
264 apm
->set_stream_delay_ms(0);
265 apm
->ProcessStream(&out_frame
);
267 if (ec
->params
.priv
.webrtc
.agc
) {
268 pa_cvolume_set(&v
, ss
->channels
, apm
->gain_control()->stream_analog_level());
269 pa_echo_canceller_set_capture_volume(ec
, &v
);
272 memcpy(out
, out_frame
._payloadData
, ec
->params
.priv
.webrtc
.blocksize
);
275 void pa_webrtc_ec_set_drift(pa_echo_canceller
*ec
, float drift
) {
276 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
277 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
279 apm
->echo_cancellation()->set_stream_drift_samples(drift
* ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
));
282 void pa_webrtc_ec_run(pa_echo_canceller
*ec
, const uint8_t *rec
, const uint8_t *play
, uint8_t *out
) {
283 pa_webrtc_ec_play(ec
, play
);
284 pa_webrtc_ec_record(ec
, rec
, out
);
287 void pa_webrtc_ec_done(pa_echo_canceller
*ec
) {
288 if (ec
->params
.priv
.webrtc
.apm
) {
289 webrtc::AudioProcessing::Destroy((webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
);
290 ec
->params
.priv
.webrtc
.apm
= NULL
;