• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2     This file is part of PulseAudio.
3 
4     Copyright 2010 Wim Taymans <wim.taymans@gmail.com>
5 
6     Contributor: Arun Raghavan <arun.raghavan@collabora.co.uk>
7 
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.
12 
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.
17 
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/>.
20 ***/
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/modargs.h>
28 #include "echo-cancel.h"
29 
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_DEREVERB_ENABLED true
37 #define DEFAULT_ECHO_SUPPRESS_ENABLED true
38 #define DEFAULT_ECHO_SUPPRESS_ATTENUATION 0
39 
40 static const char* const valid_modargs[] = {
41     "frame_size_ms",
42     "filter_size_ms",
43     "agc",
44     "denoise",
45     "dereverb",
46     "echo_suppress",
47     "echo_suppress_attenuation",
48     "echo_suppress_attenuation_active",
49     NULL
50 };
51 
speex_ec_fixate_spec(pa_sample_spec * rec_ss,pa_channel_map * rec_map,pa_sample_spec * play_ss,pa_channel_map * play_map,pa_sample_spec * out_ss,pa_channel_map * out_map)52 static void speex_ec_fixate_spec(pa_sample_spec *rec_ss, pa_channel_map *rec_map,
53                                  pa_sample_spec *play_ss, pa_channel_map *play_map,
54                                  pa_sample_spec *out_ss, pa_channel_map *out_map) {
55     out_ss->format = PA_SAMPLE_S16NE;
56 
57     *play_ss = *out_ss;
58     *play_map = *out_map;
59     *rec_ss = *out_ss;
60     *rec_map = *out_map;
61 }
62 
pa_speex_ec_preprocessor_init(pa_echo_canceller * ec,pa_sample_spec * out_ss,uint32_t nframes,pa_modargs * ma)63 static bool pa_speex_ec_preprocessor_init(pa_echo_canceller *ec, pa_sample_spec *out_ss, uint32_t nframes, pa_modargs *ma) {
64     bool agc;
65     bool denoise;
66     bool dereverb;
67     bool echo_suppress;
68     int32_t echo_suppress_attenuation;
69     int32_t echo_suppress_attenuation_active;
70 
71     agc = DEFAULT_AGC_ENABLED;
72     if (pa_modargs_get_value_boolean(ma, "agc", &agc) < 0) {
73         pa_log("Failed to parse agc value");
74         goto fail;
75     }
76 
77     denoise = DEFAULT_DENOISE_ENABLED;
78     if (pa_modargs_get_value_boolean(ma, "denoise", &denoise) < 0) {
79         pa_log("Failed to parse denoise value");
80         goto fail;
81     }
82 
83     dereverb = DEFAULT_DEREVERB_ENABLED;
84     if (pa_modargs_get_value_boolean(ma, "dereverb", &dereverb) < 0) {
85         pa_log("Failed to parse dereverb value");
86         goto fail;
87     }
88 
89     echo_suppress = DEFAULT_ECHO_SUPPRESS_ENABLED;
90     if (pa_modargs_get_value_boolean(ma, "echo_suppress", &echo_suppress) < 0) {
91         pa_log("Failed to parse echo_suppress value");
92         goto fail;
93     }
94 
95     echo_suppress_attenuation = DEFAULT_ECHO_SUPPRESS_ATTENUATION;
96     if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation", &echo_suppress_attenuation) < 0) {
97         pa_log("Failed to parse echo_suppress_attenuation value");
98         goto fail;
99     }
100     if (echo_suppress_attenuation > 0) {
101         pa_log("echo_suppress_attenuation should be a negative dB value");
102         goto fail;
103     }
104 
105     echo_suppress_attenuation_active = DEFAULT_ECHO_SUPPRESS_ATTENUATION;
106     if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation_active", &echo_suppress_attenuation_active) < 0) {
107         pa_log("Failed to parse echo_suppress_attenuation_active value");
108         goto fail;
109     }
110     if (echo_suppress_attenuation_active > 0) {
111         pa_log("echo_suppress_attenuation_active should be a negative dB value");
112         goto fail;
113     }
114 
115     if (agc || denoise || dereverb || echo_suppress) {
116         spx_int32_t tmp;
117 
118         if (out_ss->channels != 1) {
119             pa_log("AGC, denoising, dereverb and echo suppression only work with channels=1");
120             goto fail;
121         }
122 
123         ec->params.speex.pp_state = speex_preprocess_state_init(nframes, out_ss->rate);
124 
125         tmp = agc;
126         speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_AGC, &tmp);
127 
128         tmp = denoise;
129         speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_DENOISE, &tmp);
130 
131         tmp = dereverb;
132         speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_DEREVERB, &tmp);
133 
134         if (echo_suppress) {
135             if (echo_suppress_attenuation)
136                 speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS,
137                                      &echo_suppress_attenuation);
138 
139             if (echo_suppress_attenuation_active) {
140                 speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE,
141                                      &echo_suppress_attenuation_active);
142             }
143         }
144 
145         speex_preprocess_ctl(ec->params.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_STATE,
146                              ec->params.speex.state);
147 
148         pa_log_info("Loaded speex preprocessor with params: agc=%s, denoise=%s, dereverb=%s, echo_suppress=%s",
149                     pa_yes_no(agc), pa_yes_no(denoise), pa_yes_no(dereverb), pa_yes_no(echo_suppress));
150     } else
151         pa_log_info("All preprocessing options are disabled");
152 
153     return true;
154 
155 fail:
156     return false;
157 }
158 
pa_speex_ec_init(pa_core * c,pa_echo_canceller * ec,pa_sample_spec * rec_ss,pa_channel_map * rec_map,pa_sample_spec * play_ss,pa_channel_map * play_map,pa_sample_spec * out_ss,pa_channel_map * out_map,uint32_t * nframes,const char * args)159 bool pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec,
160                       pa_sample_spec *rec_ss, pa_channel_map *rec_map,
161                       pa_sample_spec *play_ss, pa_channel_map *play_map,
162                       pa_sample_spec *out_ss, pa_channel_map *out_map,
163                       uint32_t *nframes, const char *args) {
164     int rate;
165     uint32_t frame_size_ms, filter_size_ms;
166     pa_modargs *ma;
167 
168     if (!(ma = pa_modargs_new(args, valid_modargs))) {
169         pa_log("Failed to parse submodule arguments.");
170         goto fail;
171     }
172 
173     filter_size_ms = DEFAULT_FILTER_SIZE_MS;
174     if (pa_modargs_get_value_u32(ma, "filter_size_ms", &filter_size_ms) < 0 || filter_size_ms < 1 || filter_size_ms > 2000) {
175         pa_log("Invalid filter_size_ms specification");
176         goto fail;
177     }
178 
179     frame_size_ms = DEFAULT_FRAME_SIZE_MS;
180     if (pa_modargs_get_value_u32(ma, "frame_size_ms", &frame_size_ms) < 0 || frame_size_ms < 1 || frame_size_ms > 200) {
181         pa_log("Invalid frame_size_ms specification");
182         goto fail;
183     }
184 
185     speex_ec_fixate_spec(rec_ss, rec_map, play_ss, play_map, out_ss, out_map);
186 
187     rate = out_ss->rate;
188     *nframes = pa_echo_canceller_blocksize_power2(rate, frame_size_ms);
189 
190     pa_log_debug ("Using nframes %d, channels %d, rate %d", *nframes, out_ss->channels, out_ss->rate);
191     ec->params.speex.state = speex_echo_state_init_mc(*nframes, (rate * filter_size_ms) / 1000, out_ss->channels, out_ss->channels);
192 
193     if (!ec->params.speex.state)
194         goto fail;
195 
196     speex_echo_ctl(ec->params.speex.state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate);
197 
198     if (!pa_speex_ec_preprocessor_init(ec, out_ss, *nframes, ma))
199         goto fail;
200 
201     pa_modargs_free(ma);
202     return true;
203 
204 fail:
205     if (ma)
206         pa_modargs_free(ma);
207     if (ec->params.speex.pp_state) {
208         speex_preprocess_state_destroy(ec->params.speex.pp_state);
209         ec->params.speex.pp_state = NULL;
210     }
211     if (ec->params.speex.state) {
212         speex_echo_state_destroy(ec->params.speex.state);
213         ec->params.speex.state = NULL;
214     }
215     return false;
216 }
217 
pa_speex_ec_run(pa_echo_canceller * ec,const uint8_t * rec,const uint8_t * play,uint8_t * out)218 void pa_speex_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) {
219     speex_echo_cancellation(ec->params.speex.state, (const spx_int16_t *) rec, (const spx_int16_t *) play,
220                             (spx_int16_t *) out);
221 
222     /* preprecessor is run after AEC. This is not a mistake! */
223     if (ec->params.speex.pp_state)
224         speex_preprocess_run(ec->params.speex.pp_state, (spx_int16_t *) out);
225 }
226 
pa_speex_ec_done(pa_echo_canceller * ec)227 void pa_speex_ec_done(pa_echo_canceller *ec) {
228     if (ec->params.speex.pp_state) {
229         speex_preprocess_state_destroy(ec->params.speex.pp_state);
230         ec->params.speex.pp_state = NULL;
231     }
232 
233     if (ec->params.speex.state) {
234         speex_echo_state_destroy(ec->params.speex.state);
235         ec->params.speex.state = NULL;
236     }
237 }
238