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
40static 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
52static 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
63static 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
155fail:
156    return false;
157}
158
159bool 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
204fail:
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
218void 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
227void 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