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
51 static const char* const valid_modargs
[] = {
54 "analog_gain_control",
55 "digital_gain_control",
62 static int routing_mode_from_string(const char *rmode
) {
63 if (pa_streq(rmode
, "quiet-earpiece-or-headset"))
64 return webrtc::EchoControlMobile::kQuietEarpieceOrHeadset
;
65 else if (pa_streq(rmode
, "earpiece"))
66 return webrtc::EchoControlMobile::kEarpiece
;
67 else if (pa_streq(rmode
, "loud-earpiece"))
68 return webrtc::EchoControlMobile::kLoudEarpiece
;
69 else if (pa_streq(rmode
, "speakerphone"))
70 return webrtc::EchoControlMobile::kSpeakerphone
;
71 else if (pa_streq(rmode
, "loud-speakerphone"))
72 return webrtc::EchoControlMobile::kLoudSpeakerphone
;
77 pa_bool_t
pa_webrtc_ec_init(pa_core
*c
, pa_echo_canceller
*ec
,
78 pa_sample_spec
*source_ss
, pa_channel_map
*source_map
,
79 pa_sample_spec
*sink_ss
, pa_channel_map
*sink_map
,
80 uint32_t *blocksize
, const char *args
)
82 webrtc::AudioProcessing
*apm
= NULL
;
83 pa_bool_t hpf
, ns
, agc
, dgc
, mobile
, cn
;
87 if (!(ma
= pa_modargs_new(args
, valid_modargs
))) {
88 pa_log("Failed to parse submodule arguments.");
93 hpf
= DEFAULT_HIGH_PASS_FILTER
;
94 if (pa_modargs_get_value_boolean(ma
, "high_pass_filter", &hpf
) < 0) {
95 pa_log("Failed to parse high_pass_filter value");
99 ns
= DEFAULT_NOISE_SUPPRESSION
;
100 if (pa_modargs_get_value_boolean(ma
, "noise_suppression", &ns
) < 0) {
101 pa_log("Failed to parse noise_suppression value");
105 agc
= DEFAULT_ANALOG_GAIN_CONTROL
;
106 if (pa_modargs_get_value_boolean(ma
, "analog_gain_control", &agc
) < 0) {
107 pa_log("Failed to parse analog_gain_control value");
111 dgc
= DEFAULT_DIGITAL_GAIN_CONTROL
;
112 if (pa_modargs_get_value_boolean(ma
, "analog_gain_control", &dgc
) < 0) {
113 pa_log("Failed to parse digital_gain_control value");
118 pa_log("You must pick only one between analog and digital gain control");
122 mobile
= DEFAULT_MOBILE
;
123 if (pa_modargs_get_value_boolean(ma
, "mobile", &mobile
) < 0) {
124 pa_log("Failed to parse mobile value");
129 if ((rm
= routing_mode_from_string(pa_modargs_get_value(ma
, "routing_mode", DEFAULT_ROUTING_MODE
))) < 0) {
130 pa_log("Failed to parse routing_mode value");
134 cn
= DEFAULT_COMFORT_NOISE
;
135 if (pa_modargs_get_value_boolean(ma
, "comfort_noise", &cn
) < 0) {
136 pa_log("Failed to parse cn value");
140 if (pa_modargs_get_value(ma
, "comfort_noise", NULL
) || pa_modargs_get_value(ma
, "routing_mode", NULL
)) {
141 pa_log("The routing_mode and comfort_noise options are only valid with mobile=true");
146 apm
= webrtc::AudioProcessing::Create(0);
148 source_ss
->format
= PA_SAMPLE_S16NE
;
149 *sink_ss
= *source_ss
;
150 /* FIXME: the implementation actually allows a different number of
151 * source/sink channels. Do we want to support that? */
152 *sink_map
= *source_map
;
154 apm
->set_sample_rate_hz(source_ss
->rate
);
156 apm
->set_num_channels(source_ss
->channels
, source_ss
->channels
);
157 apm
->set_num_reverse_channels(sink_ss
->channels
);
160 apm
->high_pass_filter()->Enable(true);
163 apm
->echo_cancellation()->enable_drift_compensation(false);
164 apm
->echo_cancellation()->Enable(true);
166 apm
->echo_control_mobile()->set_routing_mode(static_cast<webrtc::EchoControlMobile::RoutingMode
>(rm
));
167 apm
->echo_control_mobile()->enable_comfort_noise(cn
);
168 apm
->echo_control_mobile()->Enable(true);
172 apm
->noise_suppression()->set_level(webrtc::NoiseSuppression::kHigh
);
173 apm
->noise_suppression()->Enable(true);
177 if (mobile
&& rm
<= webrtc::EchoControlMobile::kEarpiece
)
178 /* Maybe this should be a knob, but we've got a lot of knobs already */
179 apm
->gain_control()->set_mode(webrtc::GainControl::kFixedDigital
);
181 apm
->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital
);
183 /* FIXME: Hook up for analog AGC */
184 pa_log("Analog gain control isn't implemented yet -- using ditital gain control.");
185 apm
->gain_control()->set_mode(webrtc::GainControl::kAdaptiveDigital
);
189 apm
->voice_detection()->Enable(true);
191 ec
->params
.priv
.webrtc
.apm
= apm
;
192 ec
->params
.priv
.webrtc
.sample_spec
= *source_ss
;
193 ec
->params
.priv
.webrtc
.blocksize
= *blocksize
= (uint64_t)pa_bytes_per_second(source_ss
) * BLOCK_SIZE_US
/ PA_USEC_PER_SEC
;
202 webrtc::AudioProcessing::Destroy(apm
);
207 void pa_webrtc_ec_run(pa_echo_canceller
*ec
, const uint8_t *rec
, const uint8_t *play
, uint8_t *out
) {
208 webrtc::AudioProcessing
*apm
= (webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
;
209 webrtc::AudioFrame play_frame
, out_frame
;
210 const pa_sample_spec
*ss
= &ec
->params
.priv
.webrtc
.sample_spec
;
212 play_frame
._audioChannel
= ss
->channels
;
213 play_frame
._frequencyInHz
= ss
->rate
;
214 play_frame
._payloadDataLengthInSamples
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
);
215 memcpy(play_frame
._payloadData
, play
, ec
->params
.priv
.webrtc
.blocksize
);
217 out_frame
._audioChannel
= ss
->channels
;
218 out_frame
._frequencyInHz
= ss
->rate
;
219 out_frame
._payloadDataLengthInSamples
= ec
->params
.priv
.webrtc
.blocksize
/ pa_frame_size(ss
);
220 memcpy(out_frame
._payloadData
, rec
, ec
->params
.priv
.webrtc
.blocksize
);
222 apm
->AnalyzeReverseStream(&play_frame
);
223 apm
->set_stream_delay_ms(0);
224 apm
->ProcessStream(&out_frame
);
226 memcpy(out
, out_frame
._payloadData
, ec
->params
.priv
.webrtc
.blocksize
);
229 void pa_webrtc_ec_done(pa_echo_canceller
*ec
) {
230 if (ec
->params
.priv
.webrtc
.apm
) {
231 webrtc::AudioProcessing::Destroy((webrtc::AudioProcessing
*)ec
->params
.priv
.webrtc
.apm
);
232 ec
->params
.priv
.webrtc
.apm
= NULL
;