blob: 19428597019b8e80ee893e5d94f54d8ce6d81a85 [file] [log] [blame]
/* $Id$ */
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pjmedia/echo.h>
#include <pjmedia/errno.h>
#include <pjmedia/frame.h>
#include <pj/assert.h>
#include <pj/log.h>
#include <pj/pool.h>
#if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC != 0
#include <speex/speex_echo.h>
#include <speex/speex_preprocess.h>
#include "echo_internal.h"
typedef struct speex_ec
{
SpeexEchoState *state;
SpeexPreprocessState *preprocess;
unsigned samples_per_frame;
unsigned prefetch;
unsigned options;
pj_int16_t *tmp_frame;
} speex_ec;
/*
* Create the AEC.
*/
PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
unsigned clock_rate,
unsigned channel_count,
unsigned samples_per_frame,
unsigned tail_ms,
unsigned options,
void **p_echo )
{
speex_ec *echo;
int sampling_rate;
*p_echo = NULL;
echo = PJ_POOL_ZALLOC_T(pool, speex_ec);
PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM);
echo->samples_per_frame = samples_per_frame;
echo->options = options;
#if 0
echo->state = speex_echo_state_init_mc(echo->samples_per_frame,
clock_rate * tail_ms / 1000,
channel_count, channel_count);
#else
if (channel_count != 1) {
PJ_LOG(2,("echo_speex.c", "Multichannel EC is not supported by this "
"echo canceller. It may not work."));
}
echo->state = speex_echo_state_init(echo->samples_per_frame,
clock_rate * tail_ms / 1000);
#endif
if (echo->state == NULL) {
return PJ_ENOMEM;
}
/* Set sampling rate */
sampling_rate = clock_rate;
speex_echo_ctl(echo->state, SPEEX_ECHO_SET_SAMPLING_RATE,
&sampling_rate);
echo->preprocess = speex_preprocess_state_init(echo->samples_per_frame,
clock_rate);
if (echo->preprocess == NULL) {
speex_echo_state_destroy(echo->state);
return PJ_ENOMEM;
}
/* Disable all preprocessing, we only want echo cancellation */
#if 0
disabled = 0;
enabled = 1;
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DENOISE,
&enabled);
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC,
&disabled);
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_VAD,
&disabled);
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB,
&enabled);
#endif
/* Control echo cancellation in the preprocessor */
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE,
echo->state);
/* Create temporary frame for echo cancellation */
echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame);
PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM);
/* Done */
*p_echo = echo;
return PJ_SUCCESS;
}
/*
* Destroy AEC
*/
PJ_DEF(pj_status_t) speex_aec_destroy(void *state )
{
speex_ec *echo = (speex_ec*) state;
PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL);
if (echo->state) {
speex_echo_state_destroy(echo->state);
echo->state = NULL;
}
if (echo->preprocess) {
speex_preprocess_state_destroy(echo->preprocess);
echo->preprocess = NULL;
}
return PJ_SUCCESS;
}
/*
* Reset AEC
*/
PJ_DEF(void) speex_aec_reset(void *state )
{
speex_ec *echo = (speex_ec*) state;
speex_echo_state_reset(echo->state);
}
/*
* Perform echo cancellation.
*/
PJ_DEF(pj_status_t) speex_aec_cancel_echo( void *state,
pj_int16_t *rec_frm,
const pj_int16_t *play_frm,
unsigned options,
void *reserved )
{
speex_ec *echo = (speex_ec*) state;
/* Sanity checks */
PJ_ASSERT_RETURN(echo && rec_frm && play_frm && options==0 &&
reserved==NULL, PJ_EINVAL);
/* Cancel echo, put output in temporary buffer */
speex_echo_cancellation(echo->state, (const spx_int16_t*)rec_frm,
(const spx_int16_t*)play_frm,
(spx_int16_t*)echo->tmp_frame);
/* Preprocess output */
speex_preprocess_run(echo->preprocess, (spx_int16_t*)echo->tmp_frame);
/* Copy temporary buffer back to original rec_frm */
pjmedia_copy_samples(rec_frm, echo->tmp_frame, echo->samples_per_frame);
return PJ_SUCCESS;
}
#endif