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 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
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 = 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);
200 apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital);
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);
207 apm->gain_control()->Enable(true);
210 apm->voice_detection()->Enable(true);
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;
223 webrtc::AudioProcessing::Destroy(apm);
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;
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);
238 apm->AnalyzeReverseStream(&play_frame);
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;
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);
251 apm->set_stream_delay_ms(0);
252 apm->ProcessStream(&out_frame);
254 memcpy(out, out_frame._payloadData, ec->params.priv.webrtc.blocksize);
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;
261 apm->echo_cancellation()->set_stream_drift_samples(drift * ec->params.priv.webrtc.blocksize / pa_frame_size(ss));
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);
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;