2
This file is part of PulseAudio.
4
Copyright 2010 Wim Taymans <wim.taymans@gmail.com>
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, see <http://www.gnu.org/licenses/>.
26
#include <pulsecore/core-util.h>
27
#include <pulsecore/modargs.h>
28
#include "echo-cancel.h"
30
/* should be between 10-20 ms */
31
#define DEFAULT_FRAME_SIZE_MS 20
32
/* should be between 100-500 ms */
33
#define DEFAULT_FILTER_SIZE_MS 200
34
#define DEFAULT_AGC_ENABLED true
35
#define DEFAULT_DENOISE_ENABLED true
36
#define DEFAULT_ECHO_SUPPRESS_ENABLED true
37
#define DEFAULT_ECHO_SUPPRESS_ATTENUATION 0
39
static const char* const valid_modargs[] = {
45
"echo_suppress_attenuation",
46
"echo_suppress_attenuation_active",
50
static void pa_speex_ec_fixate_spec(pa_sample_spec *rec_ss, pa_channel_map *rec_map,
51
pa_sample_spec *play_ss, pa_channel_map *play_map,
52
pa_sample_spec *out_ss, pa_channel_map *out_map) {
53
out_ss->format = PA_SAMPLE_S16NE;
61
static bool pa_speex_ec_preprocessor_init(pa_echo_canceller *ec, pa_sample_spec *out_ss, uint32_t nframes, pa_modargs *ma) {
65
int32_t echo_suppress_attenuation;
66
int32_t echo_suppress_attenuation_active;
68
agc = DEFAULT_AGC_ENABLED;
69
if (pa_modargs_get_value_boolean(ma, "agc", &agc) < 0) {
70
pa_log("Failed to parse agc value");
74
denoise = DEFAULT_DENOISE_ENABLED;
75
if (pa_modargs_get_value_boolean(ma, "denoise", &denoise) < 0) {
76
pa_log("Failed to parse denoise value");
80
echo_suppress = DEFAULT_ECHO_SUPPRESS_ENABLED;
81
if (pa_modargs_get_value_boolean(ma, "echo_suppress", &echo_suppress) < 0) {
82
pa_log("Failed to parse echo_suppress value");
86
echo_suppress_attenuation = DEFAULT_ECHO_SUPPRESS_ATTENUATION;
87
if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation", &echo_suppress_attenuation) < 0) {
88
pa_log("Failed to parse echo_suppress_attenuation value");
91
if (echo_suppress_attenuation > 0) {
92
pa_log("echo_suppress_attenuation should be a negative dB value");
96
echo_suppress_attenuation_active = DEFAULT_ECHO_SUPPRESS_ATTENUATION;
97
if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation_active", &echo_suppress_attenuation_active) < 0) {
98
pa_log("Failed to parse echo_suppress_attenuation_active value");
101
if (echo_suppress_attenuation_active > 0) {
102
pa_log("echo_suppress_attenuation_active should be a negative dB value");
106
if (agc || denoise || echo_suppress) {
109
if (out_ss->channels != 1) {
110
pa_log("AGC, denoising and echo suppression only work with channels=1");
114
ec->params.priv.speex.pp_state = speex_preprocess_state_init(nframes, out_ss->rate);
117
speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_AGC, &tmp);
120
speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_DENOISE, &tmp);
123
if (echo_suppress_attenuation)
124
speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS,
125
&echo_suppress_attenuation);
127
if (echo_suppress_attenuation_active) {
128
speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE,
129
&echo_suppress_attenuation_active);
132
speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_STATE,
133
ec->params.priv.speex.state);
136
pa_log_info("Loaded speex preprocessor with params: agc=%s, denoise=%s, echo_suppress=%s", pa_yes_no(agc),
137
pa_yes_no(denoise), pa_yes_no(echo_suppress));
139
pa_log_info("All preprocessing options are disabled");
147
bool pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec,
148
pa_sample_spec *rec_ss, pa_channel_map *rec_map,
149
pa_sample_spec *play_ss, pa_channel_map *play_map,
150
pa_sample_spec *out_ss, pa_channel_map *out_map,
151
uint32_t *nframes, const char *args) {
153
uint32_t frame_size_ms, filter_size_ms;
156
if (!(ma = pa_modargs_new(args, valid_modargs))) {
157
pa_log("Failed to parse submodule arguments.");
161
filter_size_ms = DEFAULT_FILTER_SIZE_MS;
162
if (pa_modargs_get_value_u32(ma, "filter_size_ms", &filter_size_ms) < 0 || filter_size_ms < 1 || filter_size_ms > 2000) {
163
pa_log("Invalid filter_size_ms specification");
167
frame_size_ms = DEFAULT_FRAME_SIZE_MS;
168
if (pa_modargs_get_value_u32(ma, "frame_size_ms", &frame_size_ms) < 0 || frame_size_ms < 1 || frame_size_ms > 200) {
169
pa_log("Invalid frame_size_ms specification");
173
pa_speex_ec_fixate_spec(rec_ss, rec_map, play_ss, play_map, out_ss, out_map);
176
*nframes = pa_echo_canceller_blocksize_power2(rate, frame_size_ms);
178
pa_log_debug ("Using nframes %d, channels %d, rate %d", *nframes, out_ss->channels, out_ss->rate);
179
ec->params.priv.speex.state = speex_echo_state_init_mc(*nframes, (rate * filter_size_ms) / 1000, out_ss->channels, out_ss->channels);
181
if (!ec->params.priv.speex.state)
184
speex_echo_ctl(ec->params.priv.speex.state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate);
186
if (!pa_speex_ec_preprocessor_init(ec, out_ss, *nframes, ma))
195
if (ec->params.priv.speex.pp_state) {
196
speex_preprocess_state_destroy(ec->params.priv.speex.pp_state);
197
ec->params.priv.speex.pp_state = NULL;
199
if (ec->params.priv.speex.state) {
200
speex_echo_state_destroy(ec->params.priv.speex.state);
201
ec->params.priv.speex.state = NULL;
206
void pa_speex_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
207
speex_echo_cancellation(ec->params.priv.speex.state, (const spx_int16_t *) rec, (const spx_int16_t *) play,
208
(spx_int16_t *) out);
210
/* preprecessor is run after AEC. This is not a mistake! */
211
if (ec->params.priv.speex.pp_state)
212
speex_preprocess_run(ec->params.priv.speex.pp_state, (spx_int16_t *) out);
215
void pa_speex_ec_done(pa_echo_canceller *ec) {
216
if (ec->params.priv.speex.pp_state) {
217
speex_preprocess_state_destroy(ec->params.priv.speex.pp_state);
218
ec->params.priv.speex.pp_state = NULL;
221
if (ec->params.priv.speex.state) {
222
speex_echo_state_destroy(ec->params.priv.speex.state);
223
ec->params.priv.speex.state = NULL;