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