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.");
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 out_ss
->format
= PA_SAMPLE_S16NE
;
163 /* FIXME: the implementation actually allows a different number of
164 * source/sink channels. Do we want to support that? */
165 *play_map
= *out_map
;
169 apm
->set_sample_rate_hz(out_ss
->rate
);
171 apm
->set_num_channels(out_ss
->channels
, out_ss
->channels
);
172 apm
->set_num_reverse_channels(play_ss
->channels
);
175 apm
->high_pass_filter()->Enable(true);
178 if (ec
->params
.drift_compensation
) {
179 apm
->echo_cancellation()->set_device_sample_rate_hz(out_ss
->rate
);
180 apm
->echo_cancellation()->enable_drift_compensation(true);
182 apm
->echo_cancellation()->enable_drift_compensation(false);
185 apm
->echo_cancellation()->Enable(true);
187 apm
->echo_control_mobile()->set_routing_mode(static_cast<webrtc::EchoControlMobile::RoutingMode
>(rm
));
188 apm
->echo_control_mobile()->enable_comfort_noise(cn
);
189 apm
->echo_control_mobile()->Enable(true);
193 apm
->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh
);
194 apm
->noise_suppression()->Enable(true);
198 if (mobile
&& rm
<= webrtc::EchoControlMobile::kEarpiece
) {
199 /* Maybe this should be a knob, but we've got a lot of knobs already */
200 apm
->gain_control()->set_mode(webrtc::GainControl::kFixedDigital
);
201 ec
->params
.priv
.webrtc
.agc
= FALSE
;
203 apm
->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital
);
204 ec
->params
.priv
.webrtc
.agc
= FALSE
;
206 apm
->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog
);
207 if (apm
->gain_control()->set_analog_level_limits(0, PA_VOLUME_NORM
-1) != apm
->kNoError
) {
208 pa_log("Failed to initialise AGC");
211 ec
->params
.priv
.webrtc
.agc
= TRUE
;
214 apm
->gain_control()->Enable(true);
217 apm
->voice_detection()->Enable(true);
219 ec
->params
.priv
.webrtc
.apm
= apm
;
220 ec
->params
.priv
.webrtc
.sample_spec
= *out_ss
;
221 ec
->params
.priv
.webrtc
.blocksize
= (uint64_t)pa_bytes_per_second(out_ss
) * BLOCK_SIZE_US
/ PA_USEC_PER_SEC
;
222 *nframes
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(out_ss
);
231 webrtc::AudioProcessing::Destroy(apm
);
236 void pa_webrtc_ec_play(pa_echo_canceller
*ec
, const uint8_t *play
) {
237 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
238 webrtc::AudioFrame play_frame
;
239 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
241 play_frame
._audioChannel
= ss
->channels
;
242 play_frame
._frequencyInHz
= ss
->rate
;
243 play_frame
._payloadDataLengthInSamples
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
);
244 memcpy(play_frame
._payloadData
, play
, ec
->params
.priv
.webrtc
.blocksize
);
246 apm
->AnalyzeReverseStream(&play_frame
);
249 void pa_webrtc_ec_record(pa_echo_canceller
*ec
, const uint8_t *rec
, uint8_t *out
) {
250 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
251 webrtc::AudioFrame out_frame
;
252 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
255 out_frame
._audioChannel
= ss
->channels
;
256 out_frame
._frequencyInHz
= ss
->rate
;
257 out_frame
._payloadDataLengthInSamples
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
);
258 memcpy(out_frame
._payloadData
, rec
, ec
->params
.priv
.webrtc
.blocksize
);
260 if (ec
->params
.priv
.webrtc
.agc
) {
262 pa_echo_canceller_get_capture_volume(ec
, &v
);
263 apm
->gain_control()->set_stream_analog_level(pa_cvolume_avg(&v
));
266 apm
->set_stream_delay_ms(0);
267 apm
->ProcessStream(&out_frame
);
269 if (ec
->params
.priv
.webrtc
.agc
) {
270 pa_cvolume_set(&v
, ss
->channels
, apm
->gain_control()->stream_analog_level());
271 pa_echo_canceller_set_capture_volume(ec
, &v
);
274 memcpy(out
, out_frame
._payloadData
, ec
->params
.priv
.webrtc
.blocksize
);
277 void pa_webrtc_ec_set_drift(pa_echo_canceller
*ec
, float drift
) {
278 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
279 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
281 apm
->echo_cancellation()->set_stream_drift_samples(drift
* ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
));
284 void pa_webrtc_ec_run(pa_echo_canceller
*ec
, const uint8_t *rec
, const uint8_t *play
, uint8_t *out
) {
285 pa_webrtc_ec_play(ec
, play
);
286 pa_webrtc_ec_record(ec
, rec
, out
);
289 void pa_webrtc_ec_done(pa_echo_canceller
*ec
) {
290 if (ec
->params
.priv
.webrtc
.apm
) {
291 webrtc::AudioProcessing::Destroy((webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
);
292 ec
->params
.priv
.webrtc
.apm
= NULL
;