summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2013-10-21 03:11:14 +0000
committerNanang Izzuddin <nanang@teluu.com>2013-10-21 03:11:14 +0000
commitb27aac7daee03b4c7b033fe19f577e1fcd4d45f9 (patch)
tree343d5610054827e719798b05c8c4c1747cc761ae
parent4896ceeadae9253abd15e9a56fcd3b84adb80092 (diff)
Close #1705: Added playback and capture callbacks for echo canceller algo. Note that this changeset also modified current Speex AEC algo behaviors:
- applied this two APIs model for Speex AEC algo - enabled Speex AGC preprocessing git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4622 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/src/pjmedia/echo_common.c88
-rw-r--r--pjmedia/src/pjmedia/echo_internal.h5
-rw-r--r--pjmedia/src/pjmedia/echo_speex.c51
3 files changed, 115 insertions, 29 deletions
diff --git a/pjmedia/src/pjmedia/echo_common.c b/pjmedia/src/pjmedia/echo_common.c
index 191a80f1..b6bacbec 100644
--- a/pjmedia/src/pjmedia/echo_common.c
+++ b/pjmedia/src/pjmedia/echo_common.c
@@ -74,6 +74,11 @@ struct ec_operations
const pj_int16_t *play_frm,
unsigned options,
void *reserved );
+ pj_status_t (*ec_playback)(void *state,
+ pj_int16_t *play_frm );
+ pj_status_t (*ec_capture)(void *state,
+ pj_int16_t *rec_frm,
+ unsigned options );
};
@@ -98,7 +103,9 @@ static struct ec_operations speex_aec_op =
&speex_aec_create,
&speex_aec_destroy,
&speex_aec_reset,
- &speex_aec_cancel_echo
+ &speex_aec_cancel_echo,
+ &speex_aec_playback,
+ &speex_aec_capture
};
#endif
@@ -182,6 +189,11 @@ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool,
ec->op = &echo_supp_op;
}
+ /* Completeness check for EC operation playback and capture, they must
+ * be implemented both or none.
+ */
+ pj_assert(!ec->op->ec_capture == !ec->op->ec_playback);
+
PJ_LOG(5,(ec->obj_name, "Creating %s", ec->op->name));
/* Instantiate EC object */
@@ -193,35 +205,42 @@ PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool,
return status;
}
- /* Create latency buffers */
- ptime = samples_per_frame * 1000 / clock_rate;
- if (latency_ms > ptime) {
- /* Normalize latency with delaybuf/WSOLA latency */
- latency_ms -= PJ_MIN(ptime, PJMEDIA_WSOLA_DELAY_MSEC);
- }
- if (latency_ms < ptime) {
- /* Give at least one frame delay to simplify programming */
- latency_ms = ptime;
- }
- lat_cnt = latency_ms / ptime;
- while (lat_cnt--) {
- struct frame *frm;
+ /* If EC algo does not have playback and capture callbakcs,
+ * create latency buffer and delay buffer to handle drift.
+ */
+ if (ec->op->ec_playback && ec->op->ec_capture) {
+ latency_ms = 0;
+ } else {
+ /* Create latency buffers */
+ ptime = samples_per_frame * 1000 / clock_rate;
+ if (latency_ms > ptime) {
+ /* Normalize latency with delaybuf/WSOLA latency */
+ latency_ms -= PJ_MIN(ptime, PJMEDIA_WSOLA_DELAY_MSEC);
+ }
+ if (latency_ms < ptime) {
+ /* Give at least one frame delay to simplify programming */
+ latency_ms = ptime;
+ }
+ lat_cnt = latency_ms / ptime;
+ while (lat_cnt--) {
+ struct frame *frm;
- frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame<<1) +
- sizeof(struct frame));
- pj_list_push_back(&ec->lat_free, frm);
- }
+ frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame<<1) +
+ sizeof(struct frame));
+ pj_list_push_back(&ec->lat_free, frm);
+ }
- /* Create delay buffer to compensate drifts */
- if (options & PJMEDIA_ECHO_USE_SIMPLE_FIFO)
- delay_buf_opt |= PJMEDIA_DELAY_BUF_SIMPLE_FIFO;
- status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate,
- samples_per_frame, channel_count,
- (PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime,
- delay_buf_opt, &ec->delay_buf);
- if (status != PJ_SUCCESS) {
- pj_pool_release(pool);
- return status;
+ /* Create delay buffer to compensate drifts */
+ if (options & PJMEDIA_ECHO_USE_SIMPLE_FIFO)
+ delay_buf_opt |= PJMEDIA_DELAY_BUF_SIMPLE_FIFO;
+ status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate,
+ samples_per_frame, channel_count,
+ (PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime,
+ delay_buf_opt, &ec->delay_buf);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
}
PJ_LOG(4,(ec->obj_name,
@@ -267,7 +286,8 @@ PJ_DEF(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo )
pj_list_push_back(&echo->lat_free, frm);
}
echo->lat_ready = PJ_FALSE;
- pjmedia_delay_buf_reset(echo->delay_buf);
+ if (echo->delay_buf)
+ pjmedia_delay_buf_reset(echo->delay_buf);
echo->op->ec_reset(echo->state);
return PJ_SUCCESS;
}
@@ -279,6 +299,11 @@ PJ_DEF(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo )
PJ_DEF(pj_status_t) pjmedia_echo_playback( pjmedia_echo_state *echo,
pj_int16_t *play_frm )
{
+ /* If EC algo has playback handler, just pass the frame. */
+ if (echo->op->ec_playback) {
+ return (*echo->op->ec_playback)(echo->state, play_frm);
+ }
+
/* Playing frame should be stored, as it will be used by echo_capture()
* as reference frame, delay buffer is used for storing the playing frames
* as in case there was clock drift between mic & speaker.
@@ -332,6 +357,11 @@ PJ_DEF(pj_status_t) pjmedia_echo_capture( pjmedia_echo_state *echo,
struct frame *oldest_frm;
pj_status_t status, rc;
+ /* If EC algo has capture handler, just pass the frame. */
+ if (echo->op->ec_capture) {
+ return (*echo->op->ec_capture)(echo->state, rec_frm, options);
+ }
+
if (!echo->lat_ready) {
/* Prefetching to fill in the desired latency */
PJ_LOG(5,(echo->obj_name, "Prefetching.."));
diff --git a/pjmedia/src/pjmedia/echo_internal.h b/pjmedia/src/pjmedia/echo_internal.h
index c28503ba..1b0b9d57 100644
--- a/pjmedia/src/pjmedia/echo_internal.h
+++ b/pjmedia/src/pjmedia/echo_internal.h
@@ -56,6 +56,11 @@ PJ_DECL(pj_status_t) speex_aec_cancel_echo(void *state,
const pj_int16_t *play_frm,
unsigned options,
void *reserved );
+PJ_DECL(pj_status_t) speex_aec_playback(void *state,
+ pj_int16_t *play_frm );
+PJ_DECL(pj_status_t) speex_aec_capture(void *state,
+ pj_int16_t *rec_frm,
+ unsigned options );
PJ_DECL(pj_status_t) ipp_aec_create(pj_pool_t *pool,
unsigned clock_rate,
diff --git a/pjmedia/src/pjmedia/echo_speex.c b/pjmedia/src/pjmedia/echo_speex.c
index 19428597..18f42b3b 100644
--- a/pjmedia/src/pjmedia/echo_speex.c
+++ b/pjmedia/src/pjmedia/echo_speex.c
@@ -109,6 +109,13 @@ PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
&enabled);
#endif
+ /* Enable AGC */
+ {
+ spx_int32_t enabled = 1;
+ speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC,
+ &enabled);
+ }
+
/* Control echo cancellation in the preprocessor */
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE,
echo->state);
@@ -189,4 +196,48 @@ PJ_DEF(pj_status_t) speex_aec_cancel_echo( void *state,
}
+/*
+ * Let AEC know that a frame was queued to be played.
+ */
+PJ_DEF(pj_status_t) speex_aec_playback( void *state,
+ pj_int16_t *play_frm )
+{
+ speex_ec *echo = (speex_ec*) state;
+
+ /* Sanity checks */
+ PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL);
+
+ speex_echo_playback(echo->state, (spx_int16_t*)play_frm);
+
+ return PJ_SUCCESS;
+
+}
+
+/*
+ * Perform echo cancellation to captured frame.
+ */
+PJ_DEF(pj_status_t) speex_aec_capture( void *state,
+ pj_int16_t *rec_frm,
+ unsigned options )
+{
+ speex_ec *echo = (speex_ec*) state;
+
+ /* Sanity checks */
+ PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL);
+
+ PJ_UNUSED_ARG(options);
+
+ /* Cancel echo */
+ pjmedia_copy_samples(echo->tmp_frame, rec_frm, echo->samples_per_frame);
+ speex_echo_capture(echo->state,
+ (spx_int16_t*)echo->tmp_frame,
+ (spx_int16_t*)rec_frm);
+
+ /* Apply preprocessing */
+ speex_preprocess_run(echo->preprocess, (spx_int16_t*)rec_frm);
+
+ return PJ_SUCCESS;
+}
+
+
#endif