summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/build/pjmedia.dsp4
-rw-r--r--pjmedia/include/pjmedia/echo.h72
-rw-r--r--pjmedia/src/pjmedia/echo_common.c252
-rw-r--r--pjmedia/src/pjmedia/echo_internal.h31
-rw-r--r--pjmedia/src/pjmedia/echo_port.c7
-rw-r--r--pjmedia/src/pjmedia/echo_speex.c404
-rw-r--r--pjmedia/src/pjmedia/echo_suppress.c68
-rw-r--r--pjmedia/src/pjmedia/sound_port.c16
8 files changed, 336 insertions, 518 deletions
diff --git a/pjmedia/build/pjmedia.dsp b/pjmedia/build/pjmedia.dsp
index f0ce7c3e..5c8a96b1 100644
--- a/pjmedia/build/pjmedia.dsp
+++ b/pjmedia/build/pjmedia.dsp
@@ -125,6 +125,10 @@ SOURCE=..\src\pjmedia\echo_common.c
# End Source File
# Begin Source File
+SOURCE=..\src\pjmedia\echo_internal.h
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjmedia\echo_port.c
# End Source File
# Begin Source File
diff --git a/pjmedia/include/pjmedia/echo.h b/pjmedia/include/pjmedia/echo.h
index 53e262f3..4a6c28f7 100644
--- a/pjmedia/include/pjmedia/echo.h
+++ b/pjmedia/include/pjmedia/echo.h
@@ -57,18 +57,37 @@ typedef struct pjmedia_echo_state pjmedia_echo_state;
typedef enum pjmedia_echo_flag
{
/**
+ * Use any available backend echo canceller algorithm. This is
+ * the default settings. This setting is mutually exclusive with
+ * PJMEDIA_ECHO_SIMPLE and PJMEDIA_ECHO_SPEEX.
+ */
+ PJMEDIA_ECHO_DEFAULT= 0,
+
+ /**
+ * Force to use Speex AEC as the backend echo canceller algorithm.
+ * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE.
+ */
+ PJMEDIA_ECHO_SPEEX = 1,
+
+ /**
* If PJMEDIA_ECHO_SIMPLE flag is specified during echo canceller
* creation, then a simple echo suppressor will be used instead of
- * an accoustic echo cancellation.
+ * an accoustic echo cancellation. This setting is mutually exclusive
+ * with PJMEDIA_ECHO_SPEEX.
+ */
+ PJMEDIA_ECHO_SIMPLE = 2,
+
+ /**
+ * For internal use.
*/
- PJMEDIA_ECHO_SIMPLE = 1,
+ PJMEDIA_ECHO_ALGO_MASK = 15,
/**
* If PJMEDIA_ECHO_NO_LOCK flag is specified, no mutex will be created
* for the echo canceller, but application will guarantee that echo
* canceller will not be called by different threads at the same time.
*/
- PJMEDIA_ECHO_NO_LOCK = 2
+ PJMEDIA_ECHO_NO_LOCK = 16
} pjmedia_echo_flag;
@@ -102,6 +121,34 @@ PJ_DECL(pj_status_t) pjmedia_echo_create(pj_pool_t *pool,
unsigned options,
pjmedia_echo_state **p_echo );
+/**
+ * Create multi-channel the echo canceller.
+ *
+ * @param pool Pool to allocate memory.
+ * @param clock_rate Media clock rate/sampling rate.
+ * @param channel_count Number of channels.
+ * @param samples_per_frame Number of samples per frame.
+ * @param tail_ms Tail length, miliseconds.
+ * @param latency_ms Total lacency introduced by playback and
+ * recording device. Set to zero if the latency
+ * is not known.
+ * @param options Options. If PJMEDIA_ECHO_SIMPLE is specified,
+ * then a simple echo suppressor implementation
+ * will be used instead of an accoustic echo
+ * cancellation.
+ * See #pjmedia_echo_flag for other options.
+ * @param p_echo Pointer to receive the Echo Canceller state.
+ *
+ * @return PJ_SUCCESS on success, or the appropriate status.
+ */
+PJ_DECL(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned tail_ms,
+ unsigned latency_ms,
+ unsigned options,
+ pjmedia_echo_state **p_echo );
/**
* Destroy the Echo Canceller.
@@ -114,7 +161,17 @@ PJ_DECL(pj_status_t) pjmedia_echo_destroy(pjmedia_echo_state *echo );
/**
- * Let the Echo Canceller knows that a frame has been played to the speaker.
+ * Reset the echo canceller.
+ *
+ * @param echo The Echo Canceller.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo );
+
+
+/**
+ * Let the Echo Canceller know that a frame has been played to the speaker.
* The Echo Canceller will keep the frame in its internal buffer, to be used
* when cancelling the echo with #pjmedia_echo_capture().
*
@@ -131,10 +188,9 @@ PJ_DECL(pj_status_t) pjmedia_echo_playback(pjmedia_echo_state *echo,
/**
- * Let the Echo Canceller knows that a frame has been captured from
- * the microphone.
- * The Echo Canceller will cancel the echo from the captured signal,
- * using the internal buffer (supplied by #pjmedia_echo_playback())
+ * Let the Echo Canceller know that a frame has been captured from the
+ * microphone. The Echo Canceller will cancel the echo from the captured
+ * signal, using the internal buffer (supplied by #pjmedia_echo_playback())
* as the FES (Far End Speech) reference.
*
* @param echo The Echo Canceller.
diff --git a/pjmedia/src/pjmedia/echo_common.c b/pjmedia/src/pjmedia/echo_common.c
index a7e6b1bb..633e78a8 100644
--- a/pjmedia/src/pjmedia/echo_common.c
+++ b/pjmedia/src/pjmedia/echo_common.c
@@ -17,36 +17,56 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <pjmedia/config.h>
#include <pjmedia/echo.h>
+#include <pjmedia/delaybuf.h>
+#include <pjmedia/errno.h>
#include <pj/assert.h>
+#include <pj/list.h>
+#include <pj/log.h>
#include <pj/pool.h>
#include "echo_internal.h"
+#define THIS_FILE "echo_common.c"
+
typedef struct ec_operations ec_operations;
+struct frame
+{
+ PJ_DECL_LIST_MEMBER(struct frame);
+ short buf[1];
+};
+
struct pjmedia_echo_state
{
+ pj_pool_t *pool;
+ char *obj_name;
+ unsigned samples_per_frame;
void *state;
ec_operations *op;
+
+ pj_bool_t lat_ready; /* lat_buf has been filled in. */
+ unsigned lat_target_cnt;/* Target number of frames in lat_buf */
+ unsigned lat_buf_cnt; /* Actual number of frames in lat_buf */
+ struct frame lat_buf; /* Frame queue for delayed playback */
+ struct frame lat_free; /* Free frame list. */
+
+ pjmedia_delay_buf *delay_buf;
};
struct ec_operations
{
+ const char *name;
+
pj_status_t (*ec_create)(pj_pool_t *pool,
- unsigned clock_rate,
- unsigned samples_per_frame,
- unsigned tail_ms,
- unsigned latency_ms,
- unsigned options,
- void **p_state );
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned tail_ms,
+ unsigned options,
+ void **p_state );
pj_status_t (*ec_destroy)(void *state );
- 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 );
+ void (*ec_reset)(void *state );
pj_status_t (*ec_cancel)(void *state,
pj_int16_t *rec_frm,
const pj_int16_t *play_frm,
@@ -57,10 +77,10 @@ struct ec_operations
static struct ec_operations echo_supp_op =
{
+ "Echo suppressor",
&echo_supp_create,
&echo_supp_destroy,
- &echo_supp_playback,
- &echo_supp_capture,
+ &echo_supp_reset,
&echo_supp_cancel_echo
};
@@ -70,20 +90,30 @@ static struct ec_operations echo_supp_op =
* Speex AEC prototypes
*/
#if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0
-static struct ec_operations aec_op =
+static struct ec_operations speex_aec_op =
{
+ "AEC",
&speex_aec_create,
&speex_aec_destroy,
- &speex_aec_playback,
- &speex_aec_capture,
+ &speex_aec_reset,
&speex_aec_cancel_echo
};
-
-#else
-#define aec_op echo_supp_op
#endif
+/*
+ * IPP AEC prototypes
+ */
+#if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0
+static struct ec_operations ipp_aec_op =
+{
+ "IPP AEC",
+ &ipp_aec_create,
+ &ipp_aec_destroy,
+ &ipp_aec_reset,
+ &ipp_aec_cancel_echo
+};
+#endif
/*
* Create the echo canceller.
@@ -96,34 +126,106 @@ PJ_DEF(pj_status_t) pjmedia_echo_create( pj_pool_t *pool,
unsigned options,
pjmedia_echo_state **p_echo )
{
+ return pjmedia_echo_create2(pool, clock_rate, 1, samples_per_frame,
+ tail_ms, latency_ms, options, p_echo);
+}
+
+/*
+ * Create the echo canceller.
+ */
+PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned tail_ms,
+ unsigned latency_ms,
+ unsigned options,
+ pjmedia_echo_state **p_echo )
+{
+ unsigned ptime;
pjmedia_echo_state *ec;
pj_status_t status;
- /* Force to use simple echo suppressor if AEC is not available */
-#if !defined(PJMEDIA_HAS_SPEEX_AEC) || PJMEDIA_HAS_SPEEX_AEC==0
- options |= PJMEDIA_ECHO_SIMPLE;
+ /* Create new pool and instantiate and init the EC */
+ pool = pj_pool_create(pool->factory, "ec%p", 256, 256, NULL);
+ ec = PJ_POOL_ZALLOC_T(pool, struct pjmedia_echo_state);
+ ec->pool = pool;
+ ec->obj_name = pool->obj_name;
+ pj_list_init(&ec->lat_buf);
+ pj_list_init(&ec->lat_free);
+
+ /* Select the backend algorithm */
+ if (0) {
+ /* Dummy */
+ ;
+#if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0
+ } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_SPEEX ||
+ (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT)
+ {
+ ec->op = &speex_aec_op;
#endif
- ec = PJ_POOL_ZALLOC_T(pool, struct pjmedia_echo_state);
+#if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0
+ } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_IPP ||
+ (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT)
+ {
+ ec->op = &ipp_aec_op;
+
+#endif
- if (options & PJMEDIA_ECHO_SIMPLE) {
+ } else {
ec->op = &echo_supp_op;
- status = (*echo_supp_op.ec_create)(pool, clock_rate, samples_per_frame,
- tail_ms, latency_ms, options,
- &ec->state);
+ }
+
+ PJ_LOG(5,(ec->obj_name, "Creating %s", ec->op->name));
+
+ /* Instantiate EC object */
+ status = (*ec->op->ec_create)(pool, clock_rate, channel_count,
+ samples_per_frame, tail_ms,
+ options, &ec->state);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
+ return status;
+ }
+
+ /* Create latency buffers */
+ ptime = samples_per_frame * 1000 / clock_rate;
+ if (latency_ms == 0) {
+ /* Give at least one frame delay to simplify programming */
+ latency_ms = ptime;
+ }
+ ec->lat_target_cnt = latency_ms / ptime;
+ if (ec->lat_target_cnt != 0) {
+ unsigned i;
+ for (i=0; i < ec->lat_target_cnt; ++i) {
+ 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);
+ }
} else {
- ec->op = &aec_op;
- status = (*aec_op.ec_create)(pool, clock_rate,
- samples_per_frame,
- tail_ms, latency_ms, options,
- &ec->state);
+ ec->lat_ready = PJ_TRUE;
}
- if (status != PJ_SUCCESS)
+ /* Create delay buffer to compensate drifts */
+ status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate,
+ samples_per_frame, channel_count,
+ (PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime,
+ 0, &ec->delay_buf);
+ if (status != PJ_SUCCESS) {
+ pj_pool_release(pool);
return status;
+ }
- pj_assert(ec->state != NULL);
+ PJ_LOG(4,(ec->obj_name,
+ "%s created, clock_rate=%d, channel=%d, "
+ "samples per frame=%d, tail length=%d ms, "
+ "latency=%d ms",
+ ec->op->name, clock_rate, channel_count, samples_per_frame,
+ tail_ms, latency_ms));
+ /* Done */
*p_echo = ec;
return PJ_SUCCESS;
@@ -135,18 +237,63 @@ PJ_DEF(pj_status_t) pjmedia_echo_create( pj_pool_t *pool,
*/
PJ_DEF(pj_status_t) pjmedia_echo_destroy(pjmedia_echo_state *echo )
{
- return (*echo->op->ec_destroy)(echo->state);
+ (*echo->op->ec_destroy)(echo->state);
+ pj_pool_release(echo->pool);
+ return PJ_SUCCESS;
}
+/*
+ * Reset the echo canceller.
+ */
+PJ_DEF(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo )
+{
+ while (!pj_list_empty(&echo->lat_buf)) {
+ struct frame *frm;
+ frm = echo->lat_buf.next;
+ pj_list_erase(frm);
+ pj_list_push_back(&echo->lat_free, frm);
+ }
+ echo->lat_ready = PJ_FALSE;
+ pjmedia_delay_buf_reset(echo->delay_buf);
+ echo->op->ec_reset(echo->state);
+ return PJ_SUCCESS;
+}
+
/*
- * Let the Echo Canceller knows that a frame has been played to the speaker.
+ * Let the Echo Canceller know that a frame has been played to the speaker.
*/
PJ_DEF(pj_status_t) pjmedia_echo_playback( pjmedia_echo_state *echo,
pj_int16_t *play_frm )
{
- return (*echo->op->ec_playback)(echo->state, play_frm);
+ if (!echo->lat_ready) {
+ /* We've not built enough latency in the buffer, so put this frame
+ * in the latency buffer list.
+ */
+ struct frame *frm;
+
+ if (pj_list_empty(&echo->lat_free)) {
+ echo->lat_ready = PJ_TRUE;
+ PJ_LOG(5,(echo->obj_name, "Latency bufferring complete"));
+ pjmedia_delay_buf_put(echo->delay_buf, play_frm);
+ return PJ_SUCCESS;
+ }
+
+ frm = echo->lat_free.prev;
+ pj_list_erase(frm);
+
+ pjmedia_copy_samples(frm->buf, play_frm, echo->samples_per_frame);
+ pj_list_push_back(&echo->lat_buf, frm);
+
+ } else {
+ /* Latency buffer is ready (full), so we put this frame in the
+ * delay buffer.
+ */
+ pjmedia_delay_buf_put(echo->delay_buf, play_frm);
+ }
+
+ return PJ_SUCCESS;
}
@@ -158,7 +305,34 @@ PJ_DEF(pj_status_t) pjmedia_echo_capture( pjmedia_echo_state *echo,
pj_int16_t *rec_frm,
unsigned options )
{
- return (*echo->op->ec_capture)(echo->state, rec_frm, options);
+ struct frame *oldest_frm;
+ pj_status_t status, rc;
+
+ if (!echo->lat_ready) {
+ /* Prefetching to fill in the desired latency */
+ PJ_LOG(5,(echo->obj_name, "Prefetching.."));
+ return PJ_SUCCESS;
+ }
+
+ /* Retrieve oldest frame from the latency buffer */
+ oldest_frm = echo->lat_buf.next;
+ pj_list_erase(oldest_frm);
+
+ /* Cancel echo using this reference frame */
+ status = pjmedia_echo_cancel(echo, rec_frm, oldest_frm->buf,
+ options, NULL);
+
+ /* Move one frame from delay buffer to the latency buffer. */
+ rc = pjmedia_delay_buf_get(echo->delay_buf, oldest_frm->buf);
+ if (rc != PJ_SUCCESS) {
+ /* Ooops.. no frame! */
+ PJ_LOG(5,(echo->obj_name,
+ "No frame from delay buffer. This will upset EC later"));
+ pjmedia_zero_samples(oldest_frm->buf, echo->samples_per_frame);
+ }
+ pj_list_push_back(&echo->lat_buf, oldest_frm);
+
+ return status;
}
diff --git a/pjmedia/src/pjmedia/echo_internal.h b/pjmedia/src/pjmedia/echo_internal.h
index c382abbe..6b6a4b60 100644
--- a/pjmedia/src/pjmedia/echo_internal.h
+++ b/pjmedia/src/pjmedia/echo_internal.h
@@ -28,17 +28,13 @@ PJ_BEGIN_DECL
*/
PJ_DECL(pj_status_t) echo_supp_create(pj_pool_t *pool,
unsigned clock_rate,
+ unsigned channel_count,
unsigned samples_per_frame,
unsigned tail_ms,
- unsigned latency_ms,
unsigned options,
void **p_state );
PJ_DECL(pj_status_t) echo_supp_destroy(void *state);
-PJ_DECL(pj_status_t) echo_supp_playback(void *state,
- pj_int16_t *play_frm );
-PJ_DECL(pj_status_t) echo_supp_capture(void *state,
- pj_int16_t *rec_frm,
- unsigned options );
+PJ_DECL(void) echo_supp_reset(void *state);
PJ_DECL(pj_status_t) echo_supp_cancel_echo(void *state,
pj_int16_t *rec_frm,
const pj_int16_t *play_frm,
@@ -47,23 +43,34 @@ PJ_DECL(pj_status_t) echo_supp_cancel_echo(void *state,
PJ_DECL(pj_status_t) speex_aec_create(pj_pool_t *pool,
unsigned clock_rate,
+ unsigned channel_count,
unsigned samples_per_frame,
unsigned tail_ms,
- unsigned latency_ms,
unsigned options,
void **p_state );
PJ_DECL(pj_status_t) speex_aec_destroy(void *state );
-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(void) speex_aec_reset(void *state );
PJ_DECL(pj_status_t) speex_aec_cancel_echo(void *state,
pj_int16_t *rec_frm,
const pj_int16_t *play_frm,
unsigned options,
void *reserved );
+PJ_DECL(pj_status_t) ipp_aec_create(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned tail_ms,
+ unsigned options,
+ void **p_echo );
+PJ_DECL(pj_status_t) ipp_aec_destroy(void *state );
+PJ_DECL(void) ipp_aec_reset(void *state );
+PJ_DECL(pj_status_t) ipp_aec_cancel_echo(void *state,
+ pj_int16_t *rec_frm,
+ const pj_int16_t *play_frm,
+ unsigned options,
+ void *reserved );
+
PJ_END_DECL
diff --git a/pjmedia/src/pjmedia/echo_port.c b/pjmedia/src/pjmedia/echo_port.c
index 5d36e134..1b1c89c9 100644
--- a/pjmedia/src/pjmedia/echo_port.c
+++ b/pjmedia/src/pjmedia/echo_port.c
@@ -67,9 +67,10 @@ PJ_DEF(pj_status_t) pjmedia_echo_port_create(pj_pool_t *pool,
dn_port->info.bits_per_sample,
dn_port->info.samples_per_frame);
- status = pjmedia_echo_create(pool, dn_port->info.clock_rate,
- dn_port->info.samples_per_frame,
- tail_ms, latency_ms, options, &ec->ec);
+ status = pjmedia_echo_create2(pool, dn_port->info.clock_rate,
+ dn_port->info.channel_count,
+ dn_port->info.samples_per_frame,
+ tail_ms, latency_ms, options, &ec->ec);
if (status != PJ_SUCCESS)
return status;
diff --git a/pjmedia/src/pjmedia/echo_speex.c b/pjmedia/src/pjmedia/echo_speex.c
index d7f27507..033597e8 100644
--- a/pjmedia/src/pjmedia/echo_speex.c
+++ b/pjmedia/src/pjmedia/echo_speex.c
@@ -19,221 +19,13 @@
#include <pjmedia/echo.h>
#include <pjmedia/errno.h>
-#include <pjmedia/silencedet.h>
#include <pj/assert.h>
-#include <pj/lock.h>
-#include <pj/log.h>
-#include <pj/os.h>
#include <pj/pool.h>
#include <speex/speex_echo.h>
#include <speex/speex_preprocess.h>
#include "echo_internal.h"
-#define THIS_FILE "echo_speex.c"
-#define BUF_COUNT PJMEDIA_SOUND_BUFFER_COUNT
-#define MIN_PREFETCH 2
-#define MAX_PREFETCH (BUF_COUNT*2/3)
-
-
-
-#if 0
-# define TRACE_(expr) PJ_LOG(5,expr)
-#else
-# define TRACE_(expr)
-#endif
-
-
-typedef struct pjmedia_frame_queue pjmedia_frame_queue;
-
-struct fq_frame
-{
- PJ_DECL_LIST_MEMBER(struct fq_frame);
- void *buf;
- unsigned size;
- pj_uint32_t seq;
-};
-
-struct pjmedia_frame_queue
-{
- char obj_name[PJ_MAX_OBJ_NAME];
- unsigned frame_size;
- int samples_per_frame;
- unsigned count;
- unsigned max_count;
- struct fq_frame frame_list;
- struct fq_frame free_list;
-
- int seq_delay;
- int prefetch_count;
-};
-
-PJ_DEF(pj_status_t) pjmedia_frame_queue_create( pj_pool_t *pool,
- const char *name,
- unsigned frame_size,
- unsigned samples_per_frame,
- unsigned max_count,
- pjmedia_frame_queue **p_fq)
-{
- pjmedia_frame_queue *fq;
- unsigned i;
-
- fq = PJ_POOL_ZALLOC_T(pool, pjmedia_frame_queue);
-
- pj_ansi_snprintf(fq->obj_name, sizeof(fq->obj_name), name, fq);
- fq->obj_name[sizeof(fq->obj_name)-1] = '\0';
-
- fq->max_count = max_count;
- fq->frame_size = frame_size;
- fq->samples_per_frame = samples_per_frame;
- fq->count = 0;
-
- pj_list_init(&fq->frame_list);
- pj_list_init(&fq->free_list);
-
- for (i=0; i<max_count; ++i) {
- struct fq_frame *f;
-
- f = PJ_POOL_ZALLOC_T(pool, struct fq_frame);
- f->buf = pj_pool_alloc(pool, frame_size);
-
- pj_list_push_back(&fq->free_list, f);
-
- }
-
- *p_fq = fq;
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_frame_queue_init( pjmedia_frame_queue *fq,
- int seq_delay,
- int prefetch_count)
-{
- if (prefetch_count > MAX_PREFETCH)
- prefetch_count = MAX_PREFETCH;
-
- fq->seq_delay = seq_delay;
- fq->prefetch_count = prefetch_count;
- fq->count = 0;
- pj_list_merge_first(&fq->free_list, &fq->frame_list);
-
- PJ_LOG(5,(fq->obj_name, "AEC reset, delay=%d, prefetch=%d",
- fq->seq_delay, fq->prefetch_count));
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_bool_t) pjmedia_frame_queue_empty( pjmedia_frame_queue *fq )
-{
- return pj_list_empty(&fq->frame_list);
-}
-
-PJ_DEF(int) pjmedia_frame_queue_get_prefetch( pjmedia_frame_queue *fq )
-{
- return fq->prefetch_count;
-}
-
-PJ_DEF(pj_status_t) pjmedia_frame_queue_put( pjmedia_frame_queue *fq,
- const void *framebuf,
- unsigned size,
- pj_uint32_t timestamp )
-{
- struct fq_frame *f;
-
- TRACE_((fq->obj_name, "PUT seq=%d, count=%d",
- timestamp / fq->samples_per_frame, fq->count));
-
- if (pj_list_empty(&fq->free_list)) {
- PJ_LOG(5,(fq->obj_name,
- " AEC info: queue is full, frame discarded "
- "[count=%d, seq=%d]",
- fq->max_count, timestamp / fq->samples_per_frame));
- //pjmedia_frame_queue_init(fq, fq->seq_delay, fq->prefetch_count);
- return PJ_ETOOMANY;
- }
-
- PJ_ASSERT_RETURN(size <= fq->frame_size, PJ_ETOOBIG);
-
- f = fq->free_list.next;
- pj_list_erase(f);
-
- pj_memcpy(f->buf, framebuf, size);
- f->size = size;
- f->seq = timestamp / fq->samples_per_frame;
-
- pj_list_push_back(&fq->frame_list, f);
- ++fq->count;
-
- return PJ_SUCCESS;
-}
-
-PJ_DEF(pj_status_t) pjmedia_frame_queue_get( pjmedia_frame_queue *fq,
- pj_uint32_t get_timestamp,
- void **framebuf,
- unsigned *size )
-{
- pj_uint32_t frame_seq;
- struct fq_frame *f;
-
- frame_seq = get_timestamp/fq->samples_per_frame + fq->seq_delay -
- fq->prefetch_count;
-
- TRACE_((fq->obj_name, "GET seq=%d for seq=%d delay=%d, prefetch=%d",
- get_timestamp/fq->samples_per_frame, frame_seq, fq->seq_delay,
- fq->prefetch_count));
-
- *size = 0;
-
- /* Remove old frames */
- for (;!pj_list_empty(&fq->frame_list);) {
- f = fq->frame_list.next;
- if (f->seq >= frame_seq)
- break;
-
- PJ_LOG(5,(fq->obj_name,
- " AEC Info: old frame removed (seq=%d, want=%d, count=%d)",
- f->seq, frame_seq, fq->count));
- pj_list_erase(f);
- --fq->count;
- pj_list_push_back(&fq->free_list, f);
- }
-
- if (pj_list_empty(&fq->frame_list)) {
- PJ_LOG(5,(fq->obj_name,
- " AEC Info: empty queue for seq=%d!",
- frame_seq));
- return PJ_ENOTFOUND;
- }
-
- f = fq->frame_list.next;
-
- if (f->seq > frame_seq) {
- PJ_LOG(5,(fq->obj_name,
- " AEC Info: prefetching (first seq=%d)",
- f->seq));
- return -1;
- }
-
- pj_list_erase(f);
- --fq->count;
-
- *framebuf = (void*)f->buf;
- *size = f->size;
-
- TRACE_((fq->obj_name, " returning frame with seq=%d, count=%d",
- f->seq, fq->count));
-
- pj_list_push_front(&fq->free_list, f);
- return PJ_SUCCESS;
-}
-
-enum
-{
- TS_FLAG_PLAY = 1,
- TS_FLAG_REC = 2,
- TS_FLAG_OK = 3,
-};
-
typedef struct speex_ec
{
SpeexEchoState *state;
@@ -243,14 +35,6 @@ typedef struct speex_ec
unsigned prefetch;
unsigned options;
pj_int16_t *tmp_frame;
- spx_int32_t *residue;
-
- pj_uint32_t play_ts,
- rec_ts,
- ts_flag;
-
- pjmedia_frame_queue *frame_queue;
- pj_lock_t *lock; /* To protect buffers, if required */
} speex_ec;
@@ -260,43 +44,33 @@ typedef struct speex_ec
*/
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 latency_ms,
unsigned options,
void **p_echo )
{
speex_ec *echo;
int sampling_rate;
- pj_status_t status;
*p_echo = NULL;
echo = PJ_POOL_ZALLOC_T(pool, speex_ec);
PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM);
- if (options & PJMEDIA_ECHO_NO_LOCK) {
- status = pj_lock_create_null_mutex(pool, "aec%p", &echo->lock);
- if (status != PJ_SUCCESS)
- return status;
- } else {
- status = pj_lock_create_simple_mutex(pool, "aec%p", &echo->lock);
- if (status != PJ_SUCCESS)
- return status;
- }
-
echo->samples_per_frame = samples_per_frame;
- echo->prefetch = (latency_ms * clock_rate / 1000) / samples_per_frame;
- if (echo->prefetch < MIN_PREFETCH)
- echo->prefetch = MIN_PREFETCH;
- if (echo->prefetch > MAX_PREFETCH)
- echo->prefetch = MAX_PREFETCH;
echo->options = options;
- echo->state = speex_echo_state_init(samples_per_frame,
- clock_rate * tail_ms / 1000);
+#if 0
+ echo->state = speex_echo_state_init_mc(echo->samples_per_frame,
+ clock_rate * tail_ms / 1000,
+ channel_count, channel_count);
+#else
+ PJ_ASSERT_RETURN(channel_count==1, PJ_EINVAL);
+ echo->state = speex_echo_state_init(echo->samples_per_frame,
+ clock_rate * tail_ms / 1000);
+#endif
if (echo->state == NULL) {
- pj_lock_destroy(echo->lock);
return PJ_ENOMEM;
}
@@ -305,11 +79,10 @@ PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
speex_echo_ctl(echo->state, SPEEX_ECHO_SET_SAMPLING_RATE,
&sampling_rate);
- echo->preprocess = speex_preprocess_state_init(samples_per_frame,
+ echo->preprocess = speex_preprocess_state_init(echo->samples_per_frame,
clock_rate);
if (echo->preprocess == NULL) {
speex_echo_state_destroy(echo->state);
- pj_lock_destroy(echo->lock);
return PJ_ENOMEM;
}
@@ -324,7 +97,7 @@ PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_VAD,
&disabled);
speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB,
- &disabled);
+ &enabled);
#endif
/* Control echo cancellation in the preprocessor */
@@ -333,33 +106,11 @@ PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
/* Create temporary frame for echo cancellation */
- echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2 * samples_per_frame);
+ echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame);
PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM);
- /* Create temporary frame to receive residue */
- echo->residue = (spx_int32_t*)
- pj_pool_zalloc(pool, sizeof(spx_int32_t) *
- (samples_per_frame+1));
- PJ_ASSERT_RETURN(echo->residue != NULL, PJ_ENOMEM);
-
- /* Create frame queue */
- status = pjmedia_frame_queue_create(pool, "aec%p", samples_per_frame*2,
- samples_per_frame, BUF_COUNT,
- &echo->frame_queue);
- if (status != PJ_SUCCESS) {
- speex_preprocess_state_destroy(echo->preprocess);
- speex_echo_state_destroy(echo->state);
- pj_lock_destroy(echo->lock);
- return status;
- }
-
/* Done */
*p_echo = echo;
-
- PJ_LOG(4,(THIS_FILE, "Speex Echo canceller/AEC created, clock_rate=%d, "
- "samples per frame=%d, tail length=%d ms, "
- "latency=%d ms",
- clock_rate, samples_per_frame, tail_ms, latency_ms));
return PJ_SUCCESS;
}
@@ -374,9 +125,6 @@ PJ_DEF(pj_status_t) speex_aec_destroy(void *state )
PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL);
- if (echo->lock)
- pj_lock_acquire(echo->lock);
-
if (echo->state) {
speex_echo_state_destroy(echo->state);
echo->state = NULL;
@@ -387,137 +135,17 @@ PJ_DEF(pj_status_t) speex_aec_destroy(void *state )
echo->preprocess = NULL;
}
- if (echo->lock) {
- pj_lock_destroy(echo->lock);
- echo->lock = NULL;
- }
-
return PJ_SUCCESS;
}
/*
- * Let the AEC knows that a frame has been played to the speaker.
+ * Reset AEC
*/
-PJ_DEF(pj_status_t) speex_aec_playback(void *state,
- pj_int16_t *play_frm )
+PJ_DEF(void) speex_aec_reset(void *state )
{
speex_ec *echo = (speex_ec*) state;
-
- /* Sanity checks */
- PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL);
-
- /* The AEC must be configured to support internal playback buffer */
- PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP);
-
- pj_lock_acquire(echo->lock);
-
- /* Inc timestamp */
- echo->play_ts += echo->samples_per_frame;
-
- /* Initialize frame delay. */
- if ((echo->ts_flag & TS_FLAG_PLAY) == 0) {
- echo->ts_flag |= TS_FLAG_PLAY;
-
- if (echo->ts_flag == TS_FLAG_OK) {
- int seq_delay;
-
- seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /
- (int)echo->samples_per_frame;
- pjmedia_frame_queue_init(echo->frame_queue, seq_delay,
- echo->prefetch);
- }
- }
-
- if (pjmedia_frame_queue_put(echo->frame_queue, play_frm,
- echo->samples_per_frame*2,
- echo->play_ts) != PJ_SUCCESS)
- {
- int seq_delay;
-
- /* On full reset frame queue */
- seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /
- (int)echo->samples_per_frame;
- pjmedia_frame_queue_init(echo->frame_queue, seq_delay,
- echo->prefetch);
-
- /* And re-put */
- pjmedia_frame_queue_put(echo->frame_queue, play_frm,
- echo->samples_per_frame*2,
- echo->play_ts);
- }
-
- pj_lock_release(echo->lock);
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Let the AEC knows that a frame has been captured from the microphone.
- */
-PJ_DEF(pj_status_t) speex_aec_capture( void *state,
- pj_int16_t *rec_frm,
- unsigned options )
-{
- speex_ec *echo = (speex_ec*) state;
- pj_status_t status = PJ_SUCCESS;
-
- /* Sanity checks */
- PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL);
-
- /* The AEC must be configured to support internal playback buffer */
- PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP);
-
- /* Lock mutex */
- pj_lock_acquire(echo->lock);
-
- /* Inc timestamp */
- echo->rec_ts += echo->samples_per_frame;
-
- /* Init frame delay. */
- if ((echo->ts_flag & TS_FLAG_REC) == 0) {
- echo->ts_flag |= TS_FLAG_REC;
-
- if (echo->ts_flag == TS_FLAG_OK) {
- int seq_delay;
-
- seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /
- (int)echo->samples_per_frame;
- pjmedia_frame_queue_init(echo->frame_queue, seq_delay,
- echo->prefetch);
- }
- }
-
- /* Cancel echo */
- if (echo->ts_flag == TS_FLAG_OK) {
- void *play_buf;
- unsigned size = 0;
-
- if (pjmedia_frame_queue_empty(echo->frame_queue)) {
- int seq_delay;
-
- seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) /
- (int)echo->samples_per_frame;
- pjmedia_frame_queue_init(echo->frame_queue, seq_delay,
- echo->prefetch);
- status = -1;
-
- } else {
- status = pjmedia_frame_queue_get(echo->frame_queue, echo->rec_ts,
- &play_buf, &size);
- if (size != 0) {
- speex_aec_cancel_echo(echo, rec_frm, (pj_int16_t*)play_buf,
- options, NULL);
- }
- }
-
- if (status != PJ_SUCCESS)
- speex_echo_state_reset(echo->state);
- }
-
- pj_lock_release(echo->lock);
- return PJ_SUCCESS;
+ speex_echo_state_reset(echo->state);
}
diff --git a/pjmedia/src/pjmedia/echo_suppress.c b/pjmedia/src/pjmedia/echo_suppress.c
index 8ef071ba..a86a058d 100644
--- a/pjmedia/src/pjmedia/echo_suppress.c
+++ b/pjmedia/src/pjmedia/echo_suppress.c
@@ -35,9 +35,7 @@
*/
typedef struct echo_supp
{
- pj_bool_t suppressing;
pjmedia_silence_det *sd;
- pj_time_val last_signal;
unsigned samples_per_frame;
unsigned tail_ms;
} echo_supp;
@@ -49,9 +47,9 @@ typedef struct echo_supp
*/
PJ_DEF(pj_status_t) echo_supp_create( pj_pool_t *pool,
unsigned clock_rate,
+ unsigned channel_count,
unsigned samples_per_frame,
unsigned tail_ms,
- unsigned latency_ms,
unsigned options,
void **p_state )
{
@@ -59,8 +57,8 @@ PJ_DEF(pj_status_t) echo_supp_create( pj_pool_t *pool,
pj_status_t status;
PJ_UNUSED_ARG(clock_rate);
+ PJ_UNUSED_ARG(channel_count);
PJ_UNUSED_ARG(options);
- PJ_UNUSED_ARG(latency_ms);
ec = PJ_POOL_ZALLOC_T(pool, struct echo_supp);
ec->samples_per_frame = samples_per_frame;
@@ -91,68 +89,14 @@ PJ_DEF(pj_status_t) echo_supp_destroy(void *state)
/*
- * Let the AEC knows that a frame has been played to the speaker.
+ * Reset
*/
-PJ_DEF(pj_status_t) echo_supp_playback( void *state,
- pj_int16_t *play_frm )
+PJ_DEF(void) echo_supp_reset(void *state)
{
- echo_supp *ec = (echo_supp*) state;
- pj_bool_t silence;
- pj_bool_t last_suppressing = ec->suppressing;
-
- silence = pjmedia_silence_det_detect(ec->sd, play_frm,
- ec->samples_per_frame, NULL);
-
- ec->suppressing = !silence;
-
- if (ec->suppressing) {
- pj_gettimeofday(&ec->last_signal);
- }
-
- if (ec->suppressing!=0 && last_suppressing==0) {
- PJ_LOG(5,(THIS_FILE, "Start suppressing.."));
- } else if (ec->suppressing==0 && last_suppressing!=0) {
- PJ_LOG(5,(THIS_FILE, "Stop suppressing.."));
- }
-
- return PJ_SUCCESS;
-}
-
-
-/*
- * Let the AEC knows that a frame has been captured from the microphone.
- */
-PJ_DEF(pj_status_t) echo_supp_capture( void *state,
- pj_int16_t *rec_frm,
- unsigned options )
-{
- echo_supp *ec = (echo_supp*) state;
- pj_time_val now;
- unsigned delay_ms;
-
- PJ_UNUSED_ARG(options);
-
- pj_gettimeofday(&now);
-
- PJ_TIME_VAL_SUB(now, ec->last_signal);
- delay_ms = PJ_TIME_VAL_MSEC(now);
-
- if (delay_ms < ec->tail_ms) {
-#if defined(PJMEDIA_ECHO_SUPPRESS_FACTOR) && PJMEDIA_ECHO_SUPPRESS_FACTOR!=0
- unsigned i;
- for (i=0; i<ec->samples_per_frame; ++i) {
- rec_frm[i] = (pj_int16_t)(rec_frm[i] >>
- PJMEDIA_ECHO_SUPPRESS_FACTOR);
- }
-#else
- pjmedia_zero_samples(rec_frm, ec->samples_per_frame);
-#endif
- }
-
- return PJ_SUCCESS;
+ PJ_UNUSED_ARG(state);
+ return;
}
-
/*
* Perform echo cancellation.
*/
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index e180ed2b..50f35d8e 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -544,12 +544,16 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_set_ec( pjmedia_snd_port *snd_port,
if (status != PJ_SUCCESS)
si.rec_latency = si.play_latency = 0;
- delay_ms = (si.rec_latency + si.play_latency) * 1000 /
- snd_port->clock_rate;
- status = pjmedia_echo_create(pool, snd_port->clock_rate,
- snd_port->samples_per_frame,
- tail_ms, delay_ms,
- options, &snd_port->ec_state);
+ //No need to add input latency in the latency calculation,
+ //since actual input latency should be zero.
+ //delay_ms = (si.rec_latency + si.play_latency) * 1000 /
+ // snd_port->clock_rate;
+ delay_ms = si.play_latency * 1000 / snd_port->clock_rate;
+ status = pjmedia_echo_create2(pool, snd_port->clock_rate,
+ snd_port->channel_count,
+ snd_port->samples_per_frame,
+ tail_ms, delay_ms,
+ options, &snd_port->ec_state);
if (status != PJ_SUCCESS)
snd_port->ec_state = NULL;
else