summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-08-13 18:21:03 +0000
committerBenny Prijono <bennylp@teluu.com>2008-08-13 18:21:03 +0000
commitaf456c288e8d4d581aedb71c72fd1619470c2a9e (patch)
tree223c2c9a1a84c0fa961cafc79c0df9375792c7b5
parent34355a3f57bbc1f421fc200d34fb0e2599621918 (diff)
More ticket #590: optimizations in the echo suppressor, gained more than 3x speed. Added entries in mips_test
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2212 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/src/pjmedia/echo_suppress.c113
-rw-r--r--pjmedia/src/test/mips_test.c157
2 files changed, 241 insertions, 29 deletions
diff --git a/pjmedia/src/pjmedia/echo_suppress.c b/pjmedia/src/pjmedia/echo_suppress.c
index d0843784..19e48b99 100644
--- a/pjmedia/src/pjmedia/echo_suppress.c
+++ b/pjmedia/src/pjmedia/echo_suppress.c
@@ -243,6 +243,12 @@ typedef struct echo_supp
float *tmp_corr; /* Temporary corr array calculation */
float best_corr; /* Best correlation so far. */
+ unsigned sum_rec_level; /* Running sum of level in rec_hist */
+ float rec_corr; /* Running corr in rec_hist. */
+
+ unsigned sum_play_level0; /* Running sum of level for first pos */
+ float play_corr0; /* Running corr for first pos . */
+
float *min_factor; /* Array of minimum scaling factor */
float *avg_factor; /* Array of average scaling factor */
float *tmp_factor; /* Array to store provisional result */
@@ -353,6 +359,8 @@ PJ_DEF(void) echo_supp_reset(void *state)
ec->last_factor = 1.0;
ec->residue = 0;
ec->running_cnt = 0;
+ ec->sum_rec_level = ec->sum_play_level0 = 0;
+ ec->rec_corr = ec->play_corr0 = 0;
}
/*
@@ -375,6 +383,8 @@ PJ_DEF(void) echo_supp_soft_reset(void *state)
ec->best_corr = MAX_FLOAT;
ec->residue = 0;
ec->running_cnt = 0;
+ ec->sum_rec_level = ec->sum_play_level0 = 0;
+ ec->rec_corr = ec->play_corr0 = 0;
PJ_LOG(4,(THIS_FILE, "Echo suppressor soft reset. Re-learning.."));
}
@@ -403,8 +413,9 @@ static void echo_supp_update(echo_supp *ec, pj_int16_t *rec_frm,
const pj_int16_t *play_frm)
{
int prev_index;
- unsigned i, frm_level, sum_rec_level;
- float rec_corr;
+ unsigned i, j, frm_level, sum_play_level, ulaw;
+ pj_uint16_t old_rec_frm_level, old_play_frm_level;
+ float play_corr;
++ec->update_cnt;
if (ec->update_cnt > 0x7FFFFFFF)
@@ -414,6 +425,9 @@ static void echo_supp_update(echo_supp *ec, pj_int16_t *rec_frm,
frm_level = pjmedia_calc_avg_signal(play_frm, ec->samples_per_segment);
++frm_level; /* to avoid division by zero */
+ /* Save the oldest frame level for later */
+ old_play_frm_level = ec->play_hist[0];
+
/* Push current frame level to the back of the play history */
pj_array_erase(ec->play_hist, sizeof(pj_uint16_t), ec->play_hist_cnt, 0);
ec->play_hist[ec->play_hist_cnt-1] = (pj_uint16_t) frm_level;
@@ -422,6 +436,9 @@ static void echo_supp_update(echo_supp *ec, pj_int16_t *rec_frm,
frm_level = pjmedia_calc_avg_signal(rec_frm, ec->samples_per_segment);
++frm_level; /* to avoid division by zero */
+ /* Save the oldest frame level for later */
+ old_rec_frm_level = ec->rec_hist[0];
+
/* Push to the back of the rec history */
pj_array_erase(ec->rec_hist, sizeof(pj_uint16_t), ec->templ_cnt, 0);
ec->rec_hist[ec->templ_cnt-1] = (pj_uint16_t) frm_level;
@@ -437,33 +454,86 @@ static void echo_supp_update(echo_supp *ec, pj_int16_t *rec_frm,
/* Calculate rec signal pattern */
- rec_corr = 0;
- sum_rec_level = 0;
- for (i=0; i < ec->templ_cnt-1; ++i) {
- float corr;
- corr = (float)ec->rec_hist[i+1] / ec->rec_hist[i];
- rec_corr += corr;
- sum_rec_level += ec->rec_hist[i];
+ if (ec->sum_rec_level == 0) {
+ /* Buffer has just been filled up, do full calculation */
+ ec->rec_corr = 0;
+ ec->sum_rec_level = 0;
+ for (i=0; i < ec->templ_cnt-1; ++i) {
+ float corr;
+ corr = (float)ec->rec_hist[i+1] / ec->rec_hist[i];
+ ec->rec_corr += corr;
+ ec->sum_rec_level += ec->rec_hist[i];
+ }
+ ec->sum_rec_level += ec->rec_hist[i];
+ } else {
+ /* Update from previous calculation */
+ ec->sum_rec_level = ec->sum_rec_level - old_rec_frm_level +
+ ec->rec_hist[ec->templ_cnt-1];
+ ec->rec_corr = ec->rec_corr - ((float)ec->rec_hist[0] /
+ old_rec_frm_level) +
+ ((float)ec->rec_hist[ec->templ_cnt-1] /
+ ec->rec_hist[ec->templ_cnt-2]);
}
- sum_rec_level += ec->rec_hist[i];
/* Iterate through the play history and calculate the signal correlation
* for every tail position in the play_hist. Save the result in temporary
* array since we may bail out early if the conversation state is not good
* to detect echo.
*/
- for (i=0; i < ec->tail_cnt; ++i) {
- unsigned j, end, sum_play_level, ulaw;
- float play_corr = 0, corr_diff;
-
+ /*
+ * First phase: do full calculation for the first position
+ */
+ if (ec->sum_play_level0 == 0) {
+ /* Buffer has just been filled up, do full calculation */
sum_play_level = 0;
- for (j=i, end=i+ec->templ_cnt-1; j<end; ++j) {
+ play_corr = 0;
+ for (j=0; j<ec->templ_cnt-1; ++j) {
float corr;
corr = (float)ec->play_hist[j+1] / ec->play_hist[j];
play_corr += corr;
sum_play_level += ec->play_hist[j];
}
sum_play_level += ec->play_hist[j];
+ ec->sum_play_level0 = sum_play_level;
+ ec->play_corr0 = play_corr;
+ } else {
+ /* Update from previous calculation */
+ ec->sum_play_level0 = ec->sum_play_level0 - old_play_frm_level +
+ ec->play_hist[ec->templ_cnt-1];
+ ec->play_corr0 = ec->play_corr0 - ((float)ec->play_hist[0] /
+ old_play_frm_level) +
+ ((float)ec->play_hist[ec->templ_cnt-1] /
+ ec->play_hist[ec->templ_cnt-2]);
+ sum_play_level = ec->sum_play_level0;
+ play_corr = ec->play_corr0;
+ }
+ ec->tmp_corr[0] = FABS(play_corr - ec->rec_corr);
+ ec->tmp_factor[0] = (float)ec->sum_rec_level / sum_play_level;
+
+ /* Bail out if remote isn't talking */
+ ulaw = pjmedia_linear2ulaw(sum_play_level/ec->templ_cnt) ^ 0xFF;
+ if (ulaw < MIN_SIGNAL_ULAW) {
+ echo_supp_set_state(ec, ST_REM_SILENT, ulaw);
+ return;
+ }
+ /* Bail out if local user is talking */
+ if (ec->sum_rec_level > sum_play_level) {
+ echo_supp_set_state(ec, ST_LOCAL_TALK, ulaw);
+ return;
+ }
+
+ /*
+ * Second phase: do incremental calculation for the rest of positions
+ */
+ for (i=1; i < ec->tail_cnt; ++i) {
+ unsigned end;
+
+ end = i + ec->templ_cnt;
+
+ sum_play_level = sum_play_level - ec->play_hist[i-1] +
+ ec->play_hist[end-1];
+ play_corr = play_corr - ((float)ec->play_hist[i]/ec->play_hist[i-1]) +
+ ((float)ec->play_hist[end-1]/ec->play_hist[end-2]);
/* Bail out if remote isn't talking */
ulaw = pjmedia_linear2ulaw(sum_play_level/ec->templ_cnt) ^ 0xFF;
@@ -473,7 +543,7 @@ static void echo_supp_update(echo_supp *ec, pj_int16_t *rec_frm,
}
/* Bail out if local user is talking */
- if (sum_rec_level >= sum_play_level) {
+ if (ec->sum_rec_level > sum_play_level) {
echo_supp_set_state(ec, ST_LOCAL_TALK, ulaw);
return;
}
@@ -481,7 +551,7 @@ static void echo_supp_update(echo_supp *ec, pj_int16_t *rec_frm,
#if 0
// disabled: not a good idea if mic throws out loud echo
/* Also bail out if we suspect there's a doubletalk */
- ulaw = pjmedia_linear2ulaw(sum_rec_level/ec->templ_cnt) ^ 0xFF;
+ ulaw = pjmedia_linear2ulaw(ec->sum_rec_level/ec->templ_cnt) ^ 0xFF;
if (ulaw > MIN_SIGNAL_ULAW) {
echo_supp_set_state(ec, ST_DOUBLETALK, ulaw);
return;
@@ -489,11 +559,10 @@ static void echo_supp_update(echo_supp *ec, pj_int16_t *rec_frm,
#endif
/* Calculate correlation and save to temporary array */
- corr_diff = FABS(play_corr - rec_corr);
- ec->tmp_corr[i] = corr_diff;
+ ec->tmp_corr[i] = FABS(play_corr - ec->rec_corr);
/* Also calculate the gain factor between mic and speaker level */
- ec->tmp_factor[i] = (float)sum_rec_level / sum_play_level;
+ ec->tmp_factor[i] = (float)ec->sum_rec_level / sum_play_level;
pj_assert(ec->tmp_factor[i] < 1);
}
@@ -652,7 +721,7 @@ PJ_DEF(pj_status_t) echo_supp_cancel_echo( void *state,
*/
factor = 1.0;
echo_supp_set_state(ec, ST_LOCAL_TALK, rec_level);
- } else if (rec_level >= play_level) {
+ } else if (rec_level > play_level) {
/* Seems that both are talking. Scale the mic signal
* down a little bit to reduce echo, while allowing both
* parties to talk at the same time.
@@ -688,7 +757,7 @@ PJ_DEF(pj_status_t) echo_supp_cancel_echo( void *state,
if (factor >= ec->last_factor)
factor = (factor + ec->last_factor) / 2;
else
- factor = (factor + ec->last_factor*9) / 10;
+ factor = (factor + ec->last_factor*19) / 20;
/* Amplify frame */
amplify_frame(rec_frm, ec->samples_per_frame,
diff --git a/pjmedia/src/test/mips_test.c b/pjmedia/src/test/mips_test.c
index c0cd64ed..4a4c4f5b 100644
--- a/pjmedia/src/test/mips_test.c
+++ b/pjmedia/src/test/mips_test.c
@@ -51,14 +51,18 @@
ARMv7-M Cortex-M3 1.250 MIPS/MHz
*/
-//# define CPU_MHZ (2666)
-//# define CPU_IPS (CPU_MHZ * MEGA * 3.039) /* P4 2.6GHz */
+# define CPU_MHZ (2666)
+# define CPU_IPS (3.039 * CPU_MHZ * MEGA) /* P4 2.6GHz */
-# define CPU_MHZ 700
-# define CPU_IPS (700 * MEGA * 2.708) /* P3 700Mhz */
+//# define CPU_MHZ 700
+//# define CPU_IPS (700 * MEGA * 2.708) /* P3 700Mhz */
//# define CPU_MHZ 180
//# define CPU_IPS (CPU_MHZ * MEGA * 1.1) /* ARM926EJ-S */
+
+//# define CPU_MHZ 312
+//# define CPU_IPS (CPU_MHZ * MEGA * 1.282) /* Dell Axim PDA */
+
#endif
@@ -356,7 +360,7 @@ static const pj_int16_t ref_signal[] = {
};
#define THIS_FILE "mips_test.c"
-#define DURATION 1000
+#define DURATION 5000
#define PTIME 20 /* MUST be 20! */
#define MEGA 1000000
#define GIGA 1000000000
@@ -1263,7 +1267,6 @@ static pjmedia_port* ec_create(unsigned ec_tail_msec,
pjmedia_port *gen_port, *ec_port;
pj_status_t status;
- PJ_UNUSED_ARG(flags);
PJ_UNUSED_ARG(te);
gen_port = create_gen_port(pool, clock_rate, channel_count,
@@ -1272,7 +1275,7 @@ static pjmedia_port* ec_create(unsigned ec_tail_msec,
return NULL;
status = pjmedia_echo_port_create(pool, gen_port, ec_tail_msec, 0,
- 0, &ec_port);
+ flags, &ec_port);
if (status != PJ_SUCCESS)
return NULL;
@@ -1287,6 +1290,7 @@ static pjmedia_port* ec_create_100(pj_pool_t *pool,
unsigned flags,
struct test_entry *te)
{
+ flags = 0;
return ec_create(100, pool, clock_rate, channel_count, samples_per_frame,
flags, te);
}
@@ -1299,6 +1303,7 @@ static pjmedia_port* ec_create_128(pj_pool_t *pool,
unsigned flags,
struct test_entry *te)
{
+ flags = 0;
return ec_create(128, pool, clock_rate, channel_count, samples_per_frame,
flags, te);
}
@@ -1311,6 +1316,7 @@ static pjmedia_port* ec_create_200(pj_pool_t *pool,
unsigned flags,
struct test_entry *te)
{
+ flags = 0;
return ec_create(200, pool, clock_rate, channel_count, samples_per_frame,
flags, te);
}
@@ -1323,6 +1329,7 @@ static pjmedia_port* ec_create_256(pj_pool_t *pool,
unsigned flags,
struct test_entry *te)
{
+ flags = 0;
return ec_create(256, pool, clock_rate, channel_count, samples_per_frame,
flags, te);
}
@@ -1336,6 +1343,7 @@ static pjmedia_port* ec_create_400(pj_pool_t *pool,
unsigned flags,
struct test_entry *te)
{
+ flags = 0;
return ec_create(400, pool, clock_rate, channel_count, samples_per_frame,
flags, te);
}
@@ -1348,6 +1356,7 @@ static pjmedia_port* ec_create_500(pj_pool_t *pool,
unsigned flags,
struct test_entry *te)
{
+ flags = 0;
return ec_create(500, pool, clock_rate, channel_count, samples_per_frame,
flags, te);
}
@@ -1360,6 +1369,7 @@ static pjmedia_port* ec_create_512(pj_pool_t *pool,
unsigned flags,
struct test_entry *te)
{
+ flags = 0;
return ec_create(512, pool, clock_rate, channel_count, samples_per_frame,
flags, te);
}
@@ -1372,6 +1382,7 @@ static pjmedia_port* ec_create_600(pj_pool_t *pool,
unsigned flags,
struct test_entry *te)
{
+ flags = 0;
return ec_create(600, pool, clock_rate, channel_count, samples_per_frame,
flags, te);
}
@@ -1384,6 +1395,127 @@ static pjmedia_port* ec_create_800(pj_pool_t *pool,
unsigned flags,
struct test_entry *te)
{
+ flags = 0;
+ return ec_create(800, pool, clock_rate, channel_count, samples_per_frame,
+ flags, te);
+}
+
+
+
+/* Echo suppressor with 100ms tail length */
+static pjmedia_port* es_create_100(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ flags = PJMEDIA_ECHO_SIMPLE;
+ return ec_create(100, pool, clock_rate, channel_count, samples_per_frame,
+ flags, te);
+}
+
+/* Echo suppressor with 128ms tail length */
+static pjmedia_port* es_create_128(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ flags = PJMEDIA_ECHO_SIMPLE;
+ return ec_create(128, pool, clock_rate, channel_count, samples_per_frame,
+ flags, te);
+}
+
+/* Echo suppressor with 200ms tail length */
+static pjmedia_port* es_create_200(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ flags = PJMEDIA_ECHO_SIMPLE;
+ return ec_create(200, pool, clock_rate, channel_count, samples_per_frame,
+ flags, te);
+}
+
+/* Echo suppressor with 256ms tail length */
+static pjmedia_port* es_create_256(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ flags = PJMEDIA_ECHO_SIMPLE;
+ return ec_create(256, pool, clock_rate, channel_count, samples_per_frame,
+ flags, te);
+}
+
+
+/* Echo suppressor with 400ms tail length */
+static pjmedia_port* es_create_400(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ flags = PJMEDIA_ECHO_SIMPLE;
+ return ec_create(400, pool, clock_rate, channel_count, samples_per_frame,
+ flags, te);
+}
+
+/* Echo suppressor with 500ms tail length */
+static pjmedia_port* es_create_500(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ flags = PJMEDIA_ECHO_SIMPLE;
+ return ec_create(500, pool, clock_rate, channel_count, samples_per_frame,
+ flags, te);
+}
+
+/* Echo suppressor with 512ms tail length */
+static pjmedia_port* es_create_512(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ flags = PJMEDIA_ECHO_SIMPLE;
+ return ec_create(512, pool, clock_rate, channel_count, samples_per_frame,
+ flags, te);
+}
+
+/* Echo suppressor with 600ms tail length */
+static pjmedia_port* es_create_600(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ flags = PJMEDIA_ECHO_SIMPLE;
+ return ec_create(600, pool, clock_rate, channel_count, samples_per_frame,
+ flags, te);
+}
+
+/* Echo suppressor with 800ms tail length */
+static pjmedia_port* es_create_800(pj_pool_t *pool,
+ unsigned clock_rate,
+ unsigned channel_count,
+ unsigned samples_per_frame,
+ unsigned flags,
+ struct test_entry *te)
+{
+ flags = PJMEDIA_ECHO_SIMPLE;
return ec_create(800, pool, clock_rate, channel_count, samples_per_frame,
flags, te);
}
@@ -2128,6 +2260,15 @@ int mips_test(void)
{ "echo canceller 512ms tail len", OP_GET_PUT, K8|K16, &ec_create_512},
{ "echo canceller 600ms tail len", OP_GET_PUT, K8|K16, &ec_create_600},
{ "echo canceller 800ms tail len", OP_GET_PUT, K8|K16, &ec_create_800},
+ { "echo suppressor 100ms tail len", OP_GET_PUT, K8|K16, &es_create_100},
+ { "echo suppressor 128ms tail len", OP_GET_PUT, K8|K16, &es_create_128},
+ { "echo suppressor 200ms tail len", OP_GET_PUT, K8|K16, &es_create_200},
+ { "echo suppressor 256ms tail len", OP_GET_PUT, K8|K16, &es_create_256},
+ { "echo suppressor 400ms tail len", OP_GET_PUT, K8|K16, &es_create_400},
+ { "echo suppressor 500ms tail len", OP_GET_PUT, K8|K16, &es_create_500},
+ { "echo suppressor 512ms tail len", OP_GET_PUT, K8|K16, &es_create_512},
+ { "echo suppressor 600ms tail len", OP_GET_PUT, K8|K16, &es_create_600},
+ { "echo suppressor 800ms tail len", OP_GET_PUT, K8|K16, &es_create_800},
{ "tone generator with single freq", OP_GET, K8|K16, &create_tonegen1},
{ "tone generator with dual freq", OP_GET, K8|K16, &create_tonegen2},
{ "codec encode/decode - G.711", OP_PUT, K8, &g711_encode_decode},
@@ -2195,6 +2336,8 @@ int mips_test(void)
usec = (pj_elapsed_usec(&tzero, &times[0]) +
pj_elapsed_usec(&tzero, &times[1])) / 2;
+ usec = usec / (DURATION / 1000);
+
mips = (float)(CPU_IPS * usec / 1000000.0 / 1000000);
cpu_pct = (float)(100.0 * usec / 1000000);
PJ_LOG(3,(THIS_FILE, "%2dKHz %-38s % 8d %8.3f %7.2f",