diff --git a/src/modules/echo-cancel/echo-cancel.h b/src/modules/echo-cancel/echo-cancel.h index b2cf61db6..9f6798074 100644 --- a/src/modules/echo-cancel/echo-cancel.h +++ b/src/modules/echo-cancel/echo-cancel.h @@ -43,6 +43,7 @@ struct pa_echo_canceller_params { union { struct { SpeexEchoState *state; + SpeexPreprocessState *pp_state; } speex; struct { uint32_t blocksize; @@ -67,13 +68,6 @@ struct pa_echo_canceller { void (*done) (pa_echo_canceller *ec); pa_echo_canceller_params params; - - pa_bool_t agc; - pa_bool_t denoise; - pa_bool_t echo_suppress; - int32_t echo_suppress_attenuation; - int32_t echo_suppress_attenuation_active; - SpeexPreprocessState *pp_state; }; /* Speex canceller functions */ diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c index 04e5b0280..fe39baa2c 100644 --- a/src/modules/echo-cancel/module-echo-cancel.c +++ b/src/modules/echo-cancel/module-echo-cancel.c @@ -72,11 +72,6 @@ PA_MODULE_USAGE( "channel_map= " "aec_method= " "aec_args= " - "agc= " - "denoise= " - "echo_suppress= " - "echo_suppress_attenuation= " - "echo_suppress_attenuation_active= " "save_aec= " "autoloaded= " )); @@ -108,10 +103,6 @@ static const pa_echo_canceller ec_table[] = { #define DEFAULT_RATE 32000 #define DEFAULT_CHANNELS 1 #define DEFAULT_ADJUST_TIME_USEC (1*PA_USEC_PER_SEC) -#define DEFAULT_AGC_ENABLED TRUE -#define DEFAULT_DENOISE_ENABLED TRUE -#define DEFAULT_ECHO_SUPPRESS_ENABLED TRUE -#define DEFAULT_ECHO_SUPPRESS_ATTENUATION 0 #define DEFAULT_SAVE_AEC 0 #define DEFAULT_AUTOLOADED FALSE @@ -218,11 +209,6 @@ static const char* const valid_modargs[] = { "channel_map", "aec_method", "aec_args", - "agc", - "denoise", - "echo_suppress", - "echo_suppress_attenuation", - "echo_suppress_attenuation_active", "save_aec", "autoloaded", NULL @@ -727,10 +713,6 @@ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) /* perform echo cancellation */ u->ec->run(u->ec, rdata, pdata, cdata); - /* preprecessor is run after AEC. This is not a mistake! */ - if (u->ec->pp_state) - speex_preprocess_run(u->ec->pp_state, (spx_int16_t *) cdata); - if (u->save_aec) { if (u->canceled_file) fwrite(cdata, 1, u->blocksize, u->canceled_file); @@ -1407,48 +1389,6 @@ int pa__init(pa_module*m) { else u->adjust_time = DEFAULT_ADJUST_TIME_USEC; - u->ec->agc = DEFAULT_AGC_ENABLED; - if (pa_modargs_get_value_boolean(ma, "agc", &u->ec->agc) < 0) { - pa_log("Failed to parse agc value"); - goto fail; - } - - u->ec->denoise = DEFAULT_DENOISE_ENABLED; - if (pa_modargs_get_value_boolean(ma, "denoise", &u->ec->denoise) < 0) { - pa_log("Failed to parse denoise value"); - goto fail; - } - - u->ec->echo_suppress = DEFAULT_ECHO_SUPPRESS_ENABLED; - if (pa_modargs_get_value_boolean(ma, "echo_suppress", &u->ec->echo_suppress) < 0) { - pa_log("Failed to parse echo_suppress value"); - goto fail; - } - if (u->ec->echo_suppress && ec_method != PA_ECHO_CANCELLER_SPEEX) { - pa_log("Echo suppression is only useful with the speex canceller"); - goto fail; - } - - u->ec->echo_suppress_attenuation = DEFAULT_ECHO_SUPPRESS_ATTENUATION; - if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation", &u->ec->echo_suppress_attenuation) < 0) { - pa_log("Failed to parse echo_suppress_attenuation value"); - goto fail; - } - if (u->ec->echo_suppress_attenuation > 0) { - pa_log("echo_suppress_attenuation should be a negative dB value"); - goto fail; - } - - u->ec->echo_suppress_attenuation_active = DEFAULT_ECHO_SUPPRESS_ATTENUATION; - if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation_active", &u->ec->echo_suppress_attenuation_active) < 0) { - pa_log("Failed to parse echo_suppress_attenuation_active value"); - goto fail; - } - if (u->ec->echo_suppress_attenuation_active > 0) { - pa_log("echo_suppress_attenuation_active should be a negative dB value"); - goto fail; - } - u->save_aec = DEFAULT_SAVE_AEC; if (pa_modargs_get_value_u32(ma, "save_aec", &u->save_aec) < 0) { pa_log("Failed to parse save_aec value"); @@ -1470,31 +1410,6 @@ int pa__init(pa_module*m) { } } - if (u->ec->agc || u->ec->denoise || u->ec->echo_suppress) { - spx_int32_t tmp; - - if (source_ss.channels != 1) { - pa_log("AGC, denoising and echo suppression only work with channels=1"); - goto fail; - } - - u->ec->pp_state = speex_preprocess_state_init(u->blocksize / pa_frame_size(&source_ss), source_ss.rate); - - tmp = u->ec->agc; - speex_preprocess_ctl(u->ec->pp_state, SPEEX_PREPROCESS_SET_AGC, &tmp); - tmp = u->ec->denoise; - speex_preprocess_ctl(u->ec->pp_state, SPEEX_PREPROCESS_SET_DENOISE, &tmp); - if (u->ec->echo_suppress) { - if (u->ec->echo_suppress_attenuation) - speex_preprocess_ctl(u->ec->pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS, &u->ec->echo_suppress_attenuation); - if (u->ec->echo_suppress_attenuation_active) { - speex_preprocess_ctl(u->ec->pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE, - &u->ec->echo_suppress_attenuation_active); - } - speex_preprocess_ctl(u->ec->pp_state, SPEEX_PREPROCESS_SET_ECHO_STATE, u->ec->params.priv.speex.state); - } - } - /* Create source */ pa_source_new_data_init(&source_data); source_data.driver = __FILE__; @@ -1765,9 +1680,6 @@ void pa__done(pa_module*m) { if (u->sink_memblockq) pa_memblockq_free(u->sink_memblockq); - if (u->ec->pp_state) - speex_preprocess_state_destroy(u->ec->pp_state); - if (u->ec) { if (u->ec->done) u->ec->done(u->ec); diff --git a/src/modules/echo-cancel/speex.c b/src/modules/echo-cancel/speex.c index 72c52680a..d6331fc56 100644 --- a/src/modules/echo-cancel/speex.c +++ b/src/modules/echo-cancel/speex.c @@ -25,6 +25,7 @@ #include #endif +#include #include #include "echo-cancel.h" @@ -32,10 +33,19 @@ #define DEFAULT_FRAME_SIZE_MS 20 /* should be between 100-500 ms */ #define DEFAULT_FILTER_SIZE_MS 200 +#define DEFAULT_AGC_ENABLED TRUE +#define DEFAULT_DENOISE_ENABLED TRUE +#define DEFAULT_ECHO_SUPPRESS_ENABLED TRUE +#define DEFAULT_ECHO_SUPPRESS_ATTENUATION 0 static const char* const valid_modargs[] = { "frame_size_ms", "filter_size_ms", + "agc", + "denoise", + "echo_suppress", + "echo_suppress_attenuation", + "echo_suppress_attenuation_active", NULL }; @@ -48,6 +58,93 @@ static void pa_speex_ec_fixate_spec(pa_sample_spec *source_ss, pa_channel_map *s *sink_map = *source_map; } +static pa_bool_t pa_speex_ec_preprocessor_init(pa_echo_canceller *ec, pa_sample_spec *source_ss, uint32_t blocksize, pa_modargs *ma) { + pa_bool_t agc; + pa_bool_t denoise; + pa_bool_t echo_suppress; + int32_t echo_suppress_attenuation; + int32_t echo_suppress_attenuation_active; + + agc = DEFAULT_AGC_ENABLED; + if (pa_modargs_get_value_boolean(ma, "agc", &agc) < 0) { + pa_log("Failed to parse agc value"); + goto fail; + } + + denoise = DEFAULT_DENOISE_ENABLED; + if (pa_modargs_get_value_boolean(ma, "denoise", &denoise) < 0) { + pa_log("Failed to parse denoise value"); + goto fail; + } + + echo_suppress = DEFAULT_ECHO_SUPPRESS_ENABLED; + if (pa_modargs_get_value_boolean(ma, "echo_suppress", &echo_suppress) < 0) { + pa_log("Failed to parse echo_suppress value"); + goto fail; + } + + echo_suppress_attenuation = DEFAULT_ECHO_SUPPRESS_ATTENUATION; + if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation", &echo_suppress_attenuation) < 0) { + pa_log("Failed to parse echo_suppress_attenuation value"); + goto fail; + } + if (echo_suppress_attenuation > 0) { + pa_log("echo_suppress_attenuation should be a negative dB value"); + goto fail; + } + + echo_suppress_attenuation_active = DEFAULT_ECHO_SUPPRESS_ATTENUATION; + if (pa_modargs_get_value_s32(ma, "echo_suppress_attenuation_active", &echo_suppress_attenuation_active) < 0) { + pa_log("Failed to parse echo_suppress_attenuation_active value"); + goto fail; + } + if (echo_suppress_attenuation_active > 0) { + pa_log("echo_suppress_attenuation_active should be a negative dB value"); + goto fail; + } + + if (agc || denoise || echo_suppress) { + spx_int32_t tmp; + + if (source_ss->channels != 1) { + pa_log("AGC, denoising and echo suppression only work with channels=1"); + goto fail; + } + + ec->params.priv.speex.pp_state = speex_preprocess_state_init(blocksize / pa_frame_size(source_ss), source_ss->rate); + + tmp = agc; + speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_AGC, &tmp); + + tmp = denoise; + speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_DENOISE, &tmp); + + if (echo_suppress) { + if (echo_suppress_attenuation) + speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS, + &echo_suppress_attenuation); + + if (echo_suppress_attenuation_active) { + speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_SUPPRESS_ACTIVE, + &echo_suppress_attenuation_active); + } + + speex_preprocess_ctl(ec->params.priv.speex.pp_state, SPEEX_PREPROCESS_SET_ECHO_STATE, + ec->params.priv.speex.state); + } + + pa_log_info("Loaded speex preprocessor with params: agc=%s, denoise=%s, echo_suppress=%s", pa_yes_no(agc), + pa_yes_no(denoise), pa_yes_no(echo_suppress)); + } else + pa_log_info("All preprocessing options are disabled"); + + return TRUE; + +fail: + return FALSE; +} + + pa_bool_t pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec, pa_sample_spec *source_ss, pa_channel_map *source_map, pa_sample_spec *sink_ss, pa_channel_map *sink_map, @@ -95,21 +192,37 @@ pa_bool_t pa_speex_ec_init(pa_core *c, pa_echo_canceller *ec, speex_echo_ctl(ec->params.priv.speex.state, SPEEX_ECHO_SET_SAMPLING_RATE, &rate); + if (!pa_speex_ec_preprocessor_init(ec, source_ss, *blocksize, ma)) + goto fail; + pa_modargs_free(ma); return TRUE; fail: if (ma) pa_modargs_free(ma); + if (ec->params.priv.speex.state) + speex_preprocess_state_destroy(ec->params.priv.speex.pp_state); return FALSE; } void pa_speex_ec_run(pa_echo_canceller *ec, const uint8_t *rec, const uint8_t *play, uint8_t *out) { - speex_echo_cancellation(ec->params.priv.speex.state, (const spx_int16_t *) rec, (const spx_int16_t *) play, (spx_int16_t *) out); + speex_echo_cancellation(ec->params.priv.speex.state, (const spx_int16_t *) rec, (const spx_int16_t *) play, + (spx_int16_t *) out); + + /* preprecessor is run after AEC. This is not a mistake! */ + if (ec->params.priv.speex.pp_state) + speex_preprocess_run(ec->params.priv.speex.pp_state, (spx_int16_t *) out); } void pa_speex_ec_done(pa_echo_canceller *ec) { - if (ec->params.priv.speex.state) + if (ec->params.priv.speex.pp_state) { + speex_preprocess_state_destroy(ec->params.priv.speex.pp_state); + ec->params.priv.speex.pp_state = NULL; + } + + if (ec->params.priv.speex.state) { speex_echo_state_destroy(ec->params.priv.speex.state); - ec->params.priv.speex.state = NULL; + ec->params.priv.speex.state = NULL; + } }