Ignore:
Timestamp:
Aug 6, 2006 12:07:13 PM (18 years ago)
Author:
bennylp
Message:

Change AEC into generic echo canceller framework with either AEC or simple echo suppressor backend can be selected during runtime.

File:
1 moved

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/src/pjmedia/echo_speex.c

    r652 r653  
    1818 */ 
    1919 
    20 #include <pjmedia/aec.h> 
     20#include <pjmedia/echo.h> 
    2121#include <pjmedia/errno.h> 
    2222#include <pjmedia/silencedet.h> 
     
    3030 
    3131 
    32 #define THIS_FILE   "aec_speex.c" 
     32#define THIS_FILE   "echo_speex.c" 
    3333#define BUF_COUNT   8 
     34 
     35/* 
     36 * Prototypes 
     37 */ 
     38PJ_DECL(pj_status_t) speex_aec_create(pj_pool_t *pool, 
     39                                      unsigned clock_rate, 
     40                                      unsigned samples_per_frame, 
     41                                      unsigned tail_ms, 
     42                                      unsigned options, 
     43                                      void **p_state ); 
     44PJ_DECL(pj_status_t) speex_aec_destroy(void *state ); 
     45PJ_DECL(pj_status_t) speex_aec_playback(void *state, 
     46                                        pj_int16_t *play_frm ); 
     47PJ_DECL(pj_status_t) speex_aec_capture(void *state, 
     48                                       pj_int16_t *rec_frm, 
     49                                       unsigned options ); 
     50PJ_DECL(pj_status_t) speex_aec_cancel_echo(void *state, 
     51                                           pj_int16_t *rec_frm, 
     52                                           const pj_int16_t *play_frm, 
     53                                           unsigned options, 
     54                                           void *reserved ); 
    3455 
    3556 
     
    3960}; 
    4061 
    41 struct pjmedia_aec 
     62typedef struct speex_ec 
    4263{ 
    4364    SpeexEchoState       *state; 
     
    5475    unsigned         wpos;              /* Index to put newest frame.       */ 
    5576    struct frame     frames[BUF_COUNT]; /* Playback frame buffers.          */ 
    56 }; 
     77} speex_ec; 
    5778 
    5879 
     
    6182 * Create the AEC.  
    6283 */ 
    63 PJ_DEF(pj_status_t) pjmedia_aec_create( pj_pool_t *pool, 
    64                                         unsigned clock_rate, 
    65                                         unsigned samples_per_frame, 
    66                                         unsigned tail_ms, 
    67                                         unsigned options, 
    68                                         pjmedia_aec **p_aec ) 
    69 { 
    70     pjmedia_aec *aec; 
     84PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool, 
     85                                     unsigned clock_rate, 
     86                                     unsigned samples_per_frame, 
     87                                     unsigned tail_ms, 
     88                                     unsigned options, 
     89                                     void **p_echo ) 
     90{ 
     91    speex_ec *echo; 
    7192    int sampling_rate; 
    7293    unsigned i; 
     94    int disabled; 
    7395    pj_status_t status; 
    7496 
    75     *p_aec = NULL; 
    76  
    77     aec = pj_pool_zalloc(pool, sizeof(pjmedia_aec)); 
    78     PJ_ASSERT_RETURN(aec != NULL, PJ_ENOMEM); 
    79  
    80     status = pj_lock_create_simple_mutex(pool, "aec%p", &aec->lock); 
    81     if (status != PJ_SUCCESS) 
    82         return status; 
    83  
    84     aec->samples_per_frame = samples_per_frame; 
    85     aec->options = options; 
    86  
    87     aec->state = speex_echo_state_init(samples_per_frame, 
     97    *p_echo = NULL; 
     98 
     99    echo = pj_pool_zalloc(pool, sizeof(speex_ec)); 
     100    PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM); 
     101 
     102    if (options & PJMEDIA_ECHO_NO_LOCK) { 
     103        status = pj_lock_create_null_mutex(pool, "aec%p", &echo->lock); 
     104        if (status != PJ_SUCCESS) 
     105            return status; 
     106    } else { 
     107        status = pj_lock_create_simple_mutex(pool, "aec%p", &echo->lock); 
     108        if (status != PJ_SUCCESS) 
     109            return status; 
     110    } 
     111 
     112    echo->samples_per_frame = samples_per_frame; 
     113    echo->options = options; 
     114 
     115    echo->state = speex_echo_state_init(samples_per_frame, 
    88116                                        clock_rate * tail_ms / 1000); 
    89     if (aec->state == NULL) { 
    90         pj_lock_destroy(aec->lock); 
     117    if (echo->state == NULL) { 
     118        pj_lock_destroy(echo->lock); 
    91119        return PJ_ENOMEM; 
    92120    } 
    93121 
    94     aec->preprocess = speex_preprocess_state_init(samples_per_frame,  
    95                                                   clock_rate); 
    96     if (aec->preprocess == NULL) { 
    97         speex_echo_state_destroy(aec->state); 
    98         pj_lock_destroy(aec->lock); 
     122    echo->preprocess = speex_preprocess_state_init(samples_per_frame,  
     123                                                   clock_rate); 
     124    if (echo->preprocess == NULL) { 
     125        speex_echo_state_destroy(echo->state); 
     126        pj_lock_destroy(echo->lock); 
    99127        return PJ_ENOMEM; 
    100128    } 
     129 
     130    /* Disable all preprocessing, we only want echo cancellation */ 
     131    disabled = 0; 
     132    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DENOISE,  
     133                         &disabled); 
     134    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC,  
     135                         &disabled); 
     136    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_VAD,  
     137                         &disabled); 
     138    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB,  
     139                         &disabled); 
    101140 
    102141    /* Set sampling rate */ 
    103142    sampling_rate = clock_rate; 
    104     speex_echo_ctl(aec->state, SPEEX_ECHO_SET_SAMPLING_RATE,  
     143    speex_echo_ctl(echo->state, SPEEX_ECHO_SET_SAMPLING_RATE,  
    105144                   &sampling_rate); 
    106145 
    107146    /* Create temporary frame for echo cancellation */ 
    108     aec->tmp_frame = pj_pool_zalloc(pool, 2 * samples_per_frame); 
    109     PJ_ASSERT_RETURN(aec->tmp_frame != NULL, PJ_ENOMEM); 
     147    echo->tmp_frame = pj_pool_zalloc(pool, 2 * samples_per_frame); 
     148    PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM); 
    110149 
    111150    /* Create temporary frame to receive residue */ 
    112     aec->residue = pj_pool_zalloc(pool, sizeof(spx_int32_t) *  
     151    echo->residue = pj_pool_zalloc(pool, sizeof(spx_int32_t) *  
    113152                                            samples_per_frame); 
    114     PJ_ASSERT_RETURN(aec->residue != NULL, PJ_ENOMEM); 
     153    PJ_ASSERT_RETURN(echo->residue != NULL, PJ_ENOMEM); 
    115154 
    116155    /* Create internal playback buffers */ 
    117156    for (i=0; i<BUF_COUNT; ++i) { 
    118         aec->frames[i].buf = pj_pool_zalloc(pool, samples_per_frame * 2); 
    119         PJ_ASSERT_RETURN(aec->frames[i].buf != NULL, PJ_ENOMEM); 
     157        echo->frames[i].buf = pj_pool_zalloc(pool, samples_per_frame * 2); 
     158        PJ_ASSERT_RETURN(echo->frames[i].buf != NULL, PJ_ENOMEM); 
    120159    } 
    121160 
    122161 
    123162    /* Done */ 
    124     *p_aec = aec; 
    125  
    126     PJ_LOG(4,(THIS_FILE, "Echo canceller/AEC created, clock_rate=%d, " 
     163    *p_echo = echo; 
     164 
     165    PJ_LOG(4,(THIS_FILE, "Speex Echo canceller/AEC created, clock_rate=%d, " 
    127166                         "samples per frame=%d, tail length=%d ms",  
    128167                         clock_rate, 
     
    137176 * Destroy AEC 
    138177 */ 
    139 PJ_DEF(pj_status_t) pjmedia_aec_destroy(pjmedia_aec *aec ) 
    140 { 
    141     PJ_ASSERT_RETURN(aec && aec->state, PJ_EINVAL); 
    142  
    143     if (aec->lock) 
    144         pj_lock_acquire(aec->lock); 
    145  
    146     if (aec->state) { 
    147         speex_echo_state_destroy(aec->state); 
    148         aec->state = NULL; 
    149     } 
    150  
    151     if (aec->preprocess) { 
    152         speex_preprocess_state_destroy(aec->preprocess); 
    153         aec->preprocess = NULL; 
    154     } 
    155  
    156     if (aec->lock) { 
    157         pj_lock_destroy(aec->lock); 
    158         aec->lock = NULL; 
     178PJ_DEF(pj_status_t) speex_aec_destroy(void *state ) 
     179{ 
     180    speex_ec *echo = state; 
     181 
     182    PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL); 
     183 
     184    if (echo->lock) 
     185        pj_lock_acquire(echo->lock); 
     186 
     187    if (echo->state) { 
     188        speex_echo_state_destroy(echo->state); 
     189        echo->state = NULL; 
     190    } 
     191 
     192    if (echo->preprocess) { 
     193        speex_preprocess_state_destroy(echo->preprocess); 
     194        echo->preprocess = NULL; 
     195    } 
     196 
     197    if (echo->lock) { 
     198        pj_lock_destroy(echo->lock); 
     199        echo->lock = NULL; 
    159200    } 
    160201 
     
    166207 * Let the AEC knows that a frame has been played to the speaker. 
    167208 */ 
    168 PJ_DEF(pj_status_t) pjmedia_aec_playback(pjmedia_aec *aec, 
    169                                          pj_int16_t *play_frm ) 
    170 { 
     209PJ_DEF(pj_status_t) speex_aec_playback(void *state, 
     210                                       pj_int16_t *play_frm ) 
     211{ 
     212    speex_ec *echo = state; 
     213 
    171214    /* Sanity checks */ 
    172     PJ_ASSERT_RETURN(aec && play_frm, PJ_EINVAL); 
     215    PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL); 
    173216 
    174217    /* The AEC must be configured to support internal playback buffer */ 
    175     PJ_ASSERT_RETURN(aec->frames[0].buf != NULL, PJ_EINVALIDOP); 
    176  
    177     pj_lock_acquire(aec->lock); 
     218    PJ_ASSERT_RETURN(echo->frames[0].buf != NULL, PJ_EINVALIDOP); 
     219 
     220    pj_lock_acquire(echo->lock); 
    178221 
    179222    /* Check for overflows */ 
    180     if (aec->wpos == aec->rpos) { 
    181         PJ_LOG(5,(THIS_FILE, "AEC overflow (playback runs faster, " 
     223    if (echo->wpos == echo->rpos) { 
     224        PJ_LOG(5,(THIS_FILE, "Speex AEC overflow (playback runs faster, " 
    182225                             "wpos=%d, rpos=%d)", 
    183                              aec->wpos, aec->rpos)); 
    184         aec->rpos = (aec->wpos - BUF_COUNT/2) % BUF_COUNT; 
    185         speex_echo_state_reset(aec->state); 
     226                             echo->wpos, echo->rpos)); 
     227        echo->rpos = (echo->wpos - BUF_COUNT/2) % BUF_COUNT; 
     228        speex_echo_state_reset(echo->state); 
    186229    } 
    187230 
    188231    /* Save fhe frame */ 
    189     pjmedia_copy_samples(aec->frames[aec->wpos].buf, 
    190                          play_frm, aec->samples_per_frame); 
    191     aec->wpos = (aec->wpos+1) % BUF_COUNT; 
    192  
    193     pj_lock_release(aec->lock); 
     232    pjmedia_copy_samples(echo->frames[echo->wpos].buf, 
     233                         play_frm, echo->samples_per_frame); 
     234    echo->wpos = (echo->wpos+1) % BUF_COUNT; 
     235 
     236    pj_lock_release(echo->lock); 
    194237 
    195238    return PJ_SUCCESS; 
     
    200243 * Let the AEC knows that a frame has been captured from the microphone. 
    201244 */ 
    202 PJ_DEF(pj_status_t) pjmedia_aec_capture( pjmedia_aec *aec, 
    203                                          pj_int16_t *rec_frm, 
    204                                          unsigned options ) 
    205 { 
     245PJ_DEF(pj_status_t) speex_aec_capture( void *state, 
     246                                       pj_int16_t *rec_frm, 
     247                                       unsigned options ) 
     248{ 
     249    speex_ec *echo = state; 
    206250    pj_status_t status; 
    207251 
    208252    /* Sanity checks */ 
    209     PJ_ASSERT_RETURN(aec && rec_frm, PJ_EINVAL); 
     253    PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL); 
    210254 
    211255    /* The AEC must be configured to support internal playback buffer */ 
    212     PJ_ASSERT_RETURN(aec->frames[0].buf != NULL, PJ_EINVALIDOP); 
     256    PJ_ASSERT_RETURN(echo->frames[0].buf != NULL, PJ_EINVALIDOP); 
    213257 
    214258    /* Lock mutex */ 
    215     pj_lock_acquire(aec->lock); 
     259    pj_lock_acquire(echo->lock); 
    216260 
    217261 
    218262    /* Check for underflow */ 
    219     if (aec->rpos == aec->wpos) { 
     263    if (echo->rpos == echo->wpos) { 
    220264        /* Return frame as it is */ 
    221         pj_lock_release(aec->lock); 
    222  
    223         PJ_LOG(5,(THIS_FILE, "AEC underflow (capture runs faster than " 
     265        pj_lock_release(echo->lock); 
     266 
     267        PJ_LOG(5,(THIS_FILE, "Speex AEC underflow (capture runs faster than " 
    224268                             "playback, wpos=%d, rpos=%d)",  
    225                              aec->wpos, aec->rpos)); 
    226         aec->rpos = (aec->wpos - BUF_COUNT/2) % BUF_COUNT; 
    227         speex_echo_state_reset(aec->state); 
     269                             echo->wpos, echo->rpos)); 
     270        echo->rpos = (echo->wpos - BUF_COUNT/2) % BUF_COUNT; 
     271        speex_echo_state_reset(echo->state); 
    228272 
    229273        return PJ_SUCCESS; 
     
    232276 
    233277    /* Cancel echo */ 
    234     status = pjmedia_aec_cancel_echo(aec, rec_frm,  
    235                                      aec->frames[aec->rpos].buf, options, 
    236                                      NULL); 
    237  
    238     aec->rpos = (aec->rpos + 1) % BUF_COUNT; 
    239  
    240     pj_lock_release(aec->lock); 
     278    status = speex_aec_cancel_echo(echo, rec_frm,  
     279                                   echo->frames[echo->rpos].buf, options, 
     280                                   NULL); 
     281 
     282    echo->rpos = (echo->rpos + 1) % BUF_COUNT; 
     283 
     284    pj_lock_release(echo->lock); 
    241285    return status; 
    242286} 
     
    246290 * Perform echo cancellation. 
    247291 */ 
    248 PJ_DEF(pj_status_t) pjmedia_aec_cancel_echo( pjmedia_aec *aec, 
    249                                              pj_int16_t *rec_frm, 
    250                                              const pj_int16_t *play_frm, 
    251                                              unsigned options, 
    252                                              void *reserved ) 
    253 { 
     292PJ_DEF(pj_status_t) speex_aec_cancel_echo( void *state, 
     293                                           pj_int16_t *rec_frm, 
     294                                           const pj_int16_t *play_frm, 
     295                                           unsigned options, 
     296                                           void *reserved ) 
     297{ 
     298    speex_ec *echo = state; 
     299 
    254300    /* Sanity checks */ 
    255     PJ_ASSERT_RETURN(aec && rec_frm && play_frm && options==0 && 
     301    PJ_ASSERT_RETURN(echo && rec_frm && play_frm && options==0 && 
    256302                     reserved==NULL, PJ_EINVAL); 
    257303 
    258304    /* Cancel echo, put output in temporary buffer */ 
    259     speex_echo_cancel(aec->state, (const spx_int16_t*)rec_frm,  
     305    speex_echo_cancel(echo->state, (const spx_int16_t*)rec_frm,  
    260306                      (const spx_int16_t*)play_frm,  
    261                       (spx_int16_t*)aec->tmp_frame,  
    262                       aec->residue); 
     307                      (spx_int16_t*)echo->tmp_frame,  
     308                      echo->residue); 
    263309 
    264310 
    265311    /* Preprocess output */ 
    266     speex_preprocess(aec->preprocess, (spx_int16_t*)aec->tmp_frame,  
    267                      aec->residue); 
     312    speex_preprocess(echo->preprocess, (spx_int16_t*)echo->tmp_frame,  
     313                     echo->residue); 
    268314 
    269315    /* Copy temporary buffer back to original rec_frm */ 
    270     pjmedia_copy_samples(rec_frm, aec->tmp_frame, aec->samples_per_frame); 
     316    pjmedia_copy_samples(rec_frm, echo->tmp_frame, echo->samples_per_frame); 
    271317 
    272318    return PJ_SUCCESS; 
Note: See TracChangeset for help on using the changeset viewer.