Changeset 2198 for pjproject/trunk/pjmedia/src/pjmedia/echo_speex.c
- Timestamp:
- Aug 9, 2008 5:40:22 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjmedia/src/pjmedia/echo_speex.c
r2039 r2198 20 20 #include <pjmedia/echo.h> 21 21 #include <pjmedia/errno.h> 22 #include <pjmedia/silencedet.h>23 22 #include <pj/assert.h> 24 #include <pj/lock.h>25 #include <pj/log.h>26 #include <pj/os.h>27 23 #include <pj/pool.h> 28 24 #include <speex/speex_echo.h> … … 30 26 31 27 #include "echo_internal.h" 32 33 #define THIS_FILE "echo_speex.c"34 #define BUF_COUNT PJMEDIA_SOUND_BUFFER_COUNT35 #define MIN_PREFETCH 236 #define MAX_PREFETCH (BUF_COUNT*2/3)37 38 39 40 #if 041 # define TRACE_(expr) PJ_LOG(5,expr)42 #else43 # define TRACE_(expr)44 #endif45 46 47 typedef struct pjmedia_frame_queue pjmedia_frame_queue;48 49 struct fq_frame50 {51 PJ_DECL_LIST_MEMBER(struct fq_frame);52 void *buf;53 unsigned size;54 pj_uint32_t seq;55 };56 57 struct pjmedia_frame_queue58 {59 char obj_name[PJ_MAX_OBJ_NAME];60 unsigned frame_size;61 int samples_per_frame;62 unsigned count;63 unsigned max_count;64 struct fq_frame frame_list;65 struct fq_frame free_list;66 67 int seq_delay;68 int prefetch_count;69 };70 71 PJ_DEF(pj_status_t) pjmedia_frame_queue_create( pj_pool_t *pool,72 const char *name,73 unsigned frame_size,74 unsigned samples_per_frame,75 unsigned max_count,76 pjmedia_frame_queue **p_fq)77 {78 pjmedia_frame_queue *fq;79 unsigned i;80 81 fq = PJ_POOL_ZALLOC_T(pool, pjmedia_frame_queue);82 83 pj_ansi_snprintf(fq->obj_name, sizeof(fq->obj_name), name, fq);84 fq->obj_name[sizeof(fq->obj_name)-1] = '\0';85 86 fq->max_count = max_count;87 fq->frame_size = frame_size;88 fq->samples_per_frame = samples_per_frame;89 fq->count = 0;90 91 pj_list_init(&fq->frame_list);92 pj_list_init(&fq->free_list);93 94 for (i=0; i<max_count; ++i) {95 struct fq_frame *f;96 97 f = PJ_POOL_ZALLOC_T(pool, struct fq_frame);98 f->buf = pj_pool_alloc(pool, frame_size);99 100 pj_list_push_back(&fq->free_list, f);101 102 }103 104 *p_fq = fq;105 return PJ_SUCCESS;106 }107 108 PJ_DEF(pj_status_t) pjmedia_frame_queue_init( pjmedia_frame_queue *fq,109 int seq_delay,110 int prefetch_count)111 {112 if (prefetch_count > MAX_PREFETCH)113 prefetch_count = MAX_PREFETCH;114 115 fq->seq_delay = seq_delay;116 fq->prefetch_count = prefetch_count;117 fq->count = 0;118 pj_list_merge_first(&fq->free_list, &fq->frame_list);119 120 PJ_LOG(5,(fq->obj_name, "AEC reset, delay=%d, prefetch=%d",121 fq->seq_delay, fq->prefetch_count));122 123 return PJ_SUCCESS;124 }125 126 PJ_DEF(pj_bool_t) pjmedia_frame_queue_empty( pjmedia_frame_queue *fq )127 {128 return pj_list_empty(&fq->frame_list);129 }130 131 PJ_DEF(int) pjmedia_frame_queue_get_prefetch( pjmedia_frame_queue *fq )132 {133 return fq->prefetch_count;134 }135 136 PJ_DEF(pj_status_t) pjmedia_frame_queue_put( pjmedia_frame_queue *fq,137 const void *framebuf,138 unsigned size,139 pj_uint32_t timestamp )140 {141 struct fq_frame *f;142 143 TRACE_((fq->obj_name, "PUT seq=%d, count=%d",144 timestamp / fq->samples_per_frame, fq->count));145 146 if (pj_list_empty(&fq->free_list)) {147 PJ_LOG(5,(fq->obj_name,148 " AEC info: queue is full, frame discarded "149 "[count=%d, seq=%d]",150 fq->max_count, timestamp / fq->samples_per_frame));151 //pjmedia_frame_queue_init(fq, fq->seq_delay, fq->prefetch_count);152 return PJ_ETOOMANY;153 }154 155 PJ_ASSERT_RETURN(size <= fq->frame_size, PJ_ETOOBIG);156 157 f = fq->free_list.next;158 pj_list_erase(f);159 160 pj_memcpy(f->buf, framebuf, size);161 f->size = size;162 f->seq = timestamp / fq->samples_per_frame;163 164 pj_list_push_back(&fq->frame_list, f);165 ++fq->count;166 167 return PJ_SUCCESS;168 }169 170 PJ_DEF(pj_status_t) pjmedia_frame_queue_get( pjmedia_frame_queue *fq,171 pj_uint32_t get_timestamp,172 void **framebuf,173 unsigned *size )174 {175 pj_uint32_t frame_seq;176 struct fq_frame *f;177 178 frame_seq = get_timestamp/fq->samples_per_frame + fq->seq_delay -179 fq->prefetch_count;180 181 TRACE_((fq->obj_name, "GET seq=%d for seq=%d delay=%d, prefetch=%d",182 get_timestamp/fq->samples_per_frame, frame_seq, fq->seq_delay,183 fq->prefetch_count));184 185 *size = 0;186 187 /* Remove old frames */188 for (;!pj_list_empty(&fq->frame_list);) {189 f = fq->frame_list.next;190 if (f->seq >= frame_seq)191 break;192 193 PJ_LOG(5,(fq->obj_name,194 " AEC Info: old frame removed (seq=%d, want=%d, count=%d)",195 f->seq, frame_seq, fq->count));196 pj_list_erase(f);197 --fq->count;198 pj_list_push_back(&fq->free_list, f);199 }200 201 if (pj_list_empty(&fq->frame_list)) {202 PJ_LOG(5,(fq->obj_name,203 " AEC Info: empty queue for seq=%d!",204 frame_seq));205 return PJ_ENOTFOUND;206 }207 208 f = fq->frame_list.next;209 210 if (f->seq > frame_seq) {211 PJ_LOG(5,(fq->obj_name,212 " AEC Info: prefetching (first seq=%d)",213 f->seq));214 return -1;215 }216 217 pj_list_erase(f);218 --fq->count;219 220 *framebuf = (void*)f->buf;221 *size = f->size;222 223 TRACE_((fq->obj_name, " returning frame with seq=%d, count=%d",224 f->seq, fq->count));225 226 pj_list_push_front(&fq->free_list, f);227 return PJ_SUCCESS;228 }229 230 enum231 {232 TS_FLAG_PLAY = 1,233 TS_FLAG_REC = 2,234 TS_FLAG_OK = 3,235 };236 28 237 29 typedef struct speex_ec … … 244 36 unsigned options; 245 37 pj_int16_t *tmp_frame; 246 spx_int32_t *residue;247 248 pj_uint32_t play_ts,249 rec_ts,250 ts_flag;251 252 pjmedia_frame_queue *frame_queue;253 pj_lock_t *lock; /* To protect buffers, if required */254 38 } speex_ec; 255 39 … … 261 45 PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool, 262 46 unsigned clock_rate, 47 unsigned channel_count, 263 48 unsigned samples_per_frame, 264 49 unsigned tail_ms, 265 unsigned latency_ms,266 50 unsigned options, 267 51 void **p_echo ) … … 269 53 speex_ec *echo; 270 54 int sampling_rate; 271 pj_status_t status;272 55 273 56 *p_echo = NULL; … … 276 59 PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM); 277 60 278 if (options & PJMEDIA_ECHO_NO_LOCK) {279 status = pj_lock_create_null_mutex(pool, "aec%p", &echo->lock);280 if (status != PJ_SUCCESS)281 return status;282 } else {283 status = pj_lock_create_simple_mutex(pool, "aec%p", &echo->lock);284 if (status != PJ_SUCCESS)285 return status;286 }287 288 61 echo->samples_per_frame = samples_per_frame; 289 echo->prefetch = (latency_ms * clock_rate / 1000) / samples_per_frame;290 if (echo->prefetch < MIN_PREFETCH)291 echo->prefetch = MIN_PREFETCH;292 if (echo->prefetch > MAX_PREFETCH)293 echo->prefetch = MAX_PREFETCH;294 62 echo->options = options; 295 63 296 echo->state = speex_echo_state_init(samples_per_frame, 297 clock_rate * tail_ms / 1000); 64 #if 0 65 echo->state = speex_echo_state_init_mc(echo->samples_per_frame, 66 clock_rate * tail_ms / 1000, 67 channel_count, channel_count); 68 #else 69 PJ_ASSERT_RETURN(channel_count==1, PJ_EINVAL); 70 echo->state = speex_echo_state_init(echo->samples_per_frame, 71 clock_rate * tail_ms / 1000); 72 #endif 298 73 if (echo->state == NULL) { 299 pj_lock_destroy(echo->lock);300 74 return PJ_ENOMEM; 301 75 } … … 306 80 &sampling_rate); 307 81 308 echo->preprocess = speex_preprocess_state_init( samples_per_frame,82 echo->preprocess = speex_preprocess_state_init(echo->samples_per_frame, 309 83 clock_rate); 310 84 if (echo->preprocess == NULL) { 311 85 speex_echo_state_destroy(echo->state); 312 pj_lock_destroy(echo->lock);313 86 return PJ_ENOMEM; 314 87 } … … 325 98 &disabled); 326 99 speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB, 327 & disabled);100 &enabled); 328 101 #endif 329 102 … … 334 107 335 108 /* Create temporary frame for echo cancellation */ 336 echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2 *samples_per_frame);109 echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame); 337 110 PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM); 338 339 /* Create temporary frame to receive residue */340 echo->residue = (spx_int32_t*)341 pj_pool_zalloc(pool, sizeof(spx_int32_t) *342 (samples_per_frame+1));343 PJ_ASSERT_RETURN(echo->residue != NULL, PJ_ENOMEM);344 345 /* Create frame queue */346 status = pjmedia_frame_queue_create(pool, "aec%p", samples_per_frame*2,347 samples_per_frame, BUF_COUNT,348 &echo->frame_queue);349 if (status != PJ_SUCCESS) {350 speex_preprocess_state_destroy(echo->preprocess);351 speex_echo_state_destroy(echo->state);352 pj_lock_destroy(echo->lock);353 return status;354 }355 111 356 112 /* Done */ 357 113 *p_echo = echo; 358 359 PJ_LOG(4,(THIS_FILE, "Speex Echo canceller/AEC created, clock_rate=%d, "360 "samples per frame=%d, tail length=%d ms, "361 "latency=%d ms",362 clock_rate, samples_per_frame, tail_ms, latency_ms));363 114 return PJ_SUCCESS; 364 115 … … 375 126 PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL); 376 127 377 if (echo->lock)378 pj_lock_acquire(echo->lock);379 380 128 if (echo->state) { 381 129 speex_echo_state_destroy(echo->state); … … 388 136 } 389 137 390 if (echo->lock) {391 pj_lock_destroy(echo->lock);392 echo->lock = NULL;393 }394 395 138 return PJ_SUCCESS; 396 139 } … … 398 141 399 142 /* 400 * Let the AEC knows that a frame has been played to the speaker.143 * Reset AEC 401 144 */ 402 PJ_DEF(pj_status_t) speex_aec_playback(void *state, 403 pj_int16_t *play_frm ) 145 PJ_DEF(void) speex_aec_reset(void *state ) 404 146 { 405 147 speex_ec *echo = (speex_ec*) state; 406 407 /* Sanity checks */ 408 PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL); 409 410 /* The AEC must be configured to support internal playback buffer */ 411 PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP); 412 413 pj_lock_acquire(echo->lock); 414 415 /* Inc timestamp */ 416 echo->play_ts += echo->samples_per_frame; 417 418 /* Initialize frame delay. */ 419 if ((echo->ts_flag & TS_FLAG_PLAY) == 0) { 420 echo->ts_flag |= TS_FLAG_PLAY; 421 422 if (echo->ts_flag == TS_FLAG_OK) { 423 int seq_delay; 424 425 seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) / 426 (int)echo->samples_per_frame; 427 pjmedia_frame_queue_init(echo->frame_queue, seq_delay, 428 echo->prefetch); 429 } 430 } 431 432 if (pjmedia_frame_queue_put(echo->frame_queue, play_frm, 433 echo->samples_per_frame*2, 434 echo->play_ts) != PJ_SUCCESS) 435 { 436 int seq_delay; 437 438 /* On full reset frame queue */ 439 seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) / 440 (int)echo->samples_per_frame; 441 pjmedia_frame_queue_init(echo->frame_queue, seq_delay, 442 echo->prefetch); 443 444 /* And re-put */ 445 pjmedia_frame_queue_put(echo->frame_queue, play_frm, 446 echo->samples_per_frame*2, 447 echo->play_ts); 448 } 449 450 pj_lock_release(echo->lock); 451 452 return PJ_SUCCESS; 453 } 454 455 456 /* 457 * Let the AEC knows that a frame has been captured from the microphone. 458 */ 459 PJ_DEF(pj_status_t) speex_aec_capture( void *state, 460 pj_int16_t *rec_frm, 461 unsigned options ) 462 { 463 speex_ec *echo = (speex_ec*) state; 464 pj_status_t status = PJ_SUCCESS; 465 466 /* Sanity checks */ 467 PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL); 468 469 /* The AEC must be configured to support internal playback buffer */ 470 PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP); 471 472 /* Lock mutex */ 473 pj_lock_acquire(echo->lock); 474 475 /* Inc timestamp */ 476 echo->rec_ts += echo->samples_per_frame; 477 478 /* Init frame delay. */ 479 if ((echo->ts_flag & TS_FLAG_REC) == 0) { 480 echo->ts_flag |= TS_FLAG_REC; 481 482 if (echo->ts_flag == TS_FLAG_OK) { 483 int seq_delay; 484 485 seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) / 486 (int)echo->samples_per_frame; 487 pjmedia_frame_queue_init(echo->frame_queue, seq_delay, 488 echo->prefetch); 489 } 490 } 491 492 /* Cancel echo */ 493 if (echo->ts_flag == TS_FLAG_OK) { 494 void *play_buf; 495 unsigned size = 0; 496 497 if (pjmedia_frame_queue_empty(echo->frame_queue)) { 498 int seq_delay; 499 500 seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) / 501 (int)echo->samples_per_frame; 502 pjmedia_frame_queue_init(echo->frame_queue, seq_delay, 503 echo->prefetch); 504 status = -1; 505 506 } else { 507 status = pjmedia_frame_queue_get(echo->frame_queue, echo->rec_ts, 508 &play_buf, &size); 509 if (size != 0) { 510 speex_aec_cancel_echo(echo, rec_frm, (pj_int16_t*)play_buf, 511 options, NULL); 512 } 513 } 514 515 if (status != PJ_SUCCESS) 516 speex_echo_state_reset(echo->state); 517 } 518 519 pj_lock_release(echo->lock); 520 return PJ_SUCCESS; 148 speex_echo_state_reset(echo->state); 521 149 } 522 150
Note: See TracChangeset
for help on using the changeset viewer.