Changeset 2198


Ignore:
Timestamp:
Aug 9, 2008 5:40:22 AM (11 years ago)
Author:
bennylp
Message:

Ticket #588: Improvements to echo cancellation framework

Location:
pjproject/trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjmedia/build/pjmedia.dsp

    r2101 r2198  
    126126# Begin Source File 
    127127 
     128SOURCE=..\src\pjmedia\echo_internal.h 
     129# End Source File 
     130# Begin Source File 
     131 
    128132SOURCE=..\src\pjmedia\echo_port.c 
    129133# End Source File 
  • pjproject/trunk/pjmedia/include/pjmedia/echo.h

    r2039 r2198  
    5858{ 
    5959    /** 
     60     * Use any available backend echo canceller algorithm. This is 
     61     * the default settings. This setting is mutually exclusive with 
     62     * PJMEDIA_ECHO_SIMPLE and PJMEDIA_ECHO_SPEEX. 
     63     */ 
     64    PJMEDIA_ECHO_DEFAULT= 0, 
     65 
     66    /** 
     67     * Force to use Speex AEC as the backend echo canceller algorithm. 
     68     * This setting is mutually exclusive with PJMEDIA_ECHO_SIMPLE. 
     69     */ 
     70    PJMEDIA_ECHO_SPEEX  = 1, 
     71 
     72    /** 
    6073     * If PJMEDIA_ECHO_SIMPLE flag is specified during echo canceller 
    6174     * creation, then a simple echo suppressor will be used instead of 
    62      * an accoustic echo cancellation. 
    63      */ 
    64     PJMEDIA_ECHO_SIMPLE = 1, 
     75     * an accoustic echo cancellation. This setting is mutually exclusive 
     76     * with PJMEDIA_ECHO_SPEEX. 
     77     */ 
     78    PJMEDIA_ECHO_SIMPLE = 2, 
     79 
     80    /** 
     81     * For internal use. 
     82     */ 
     83    PJMEDIA_ECHO_ALGO_MASK = 15, 
    6584 
    6685    /** 
     
    6988     * canceller will not be called by different threads at the same time. 
    7089     */ 
    71     PJMEDIA_ECHO_NO_LOCK = 2 
     90    PJMEDIA_ECHO_NO_LOCK = 16 
    7291 
    7392} pjmedia_echo_flag; 
     
    103122                                         pjmedia_echo_state **p_echo ); 
    104123 
     124/** 
     125 * Create multi-channel the echo canceller.  
     126 * 
     127 * @param pool              Pool to allocate memory. 
     128 * @param clock_rate        Media clock rate/sampling rate. 
     129 * @param channel_count     Number of channels. 
     130 * @param samples_per_frame Number of samples per frame. 
     131 * @param tail_ms           Tail length, miliseconds. 
     132 * @param latency_ms        Total lacency introduced by playback and  
     133 *                          recording device. Set to zero if the latency 
     134 *                          is not known. 
     135 * @param options           Options. If PJMEDIA_ECHO_SIMPLE is specified, 
     136 *                          then a simple echo suppressor implementation  
     137 *                          will be used instead of an accoustic echo  
     138 *                          cancellation. 
     139 *                          See #pjmedia_echo_flag for other options. 
     140 * @param p_echo            Pointer to receive the Echo Canceller state. 
     141 * 
     142 * @return                  PJ_SUCCESS on success, or the appropriate status. 
     143 */ 
     144PJ_DECL(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool, 
     145                                          unsigned clock_rate, 
     146                                          unsigned channel_count, 
     147                                          unsigned samples_per_frame, 
     148                                          unsigned tail_ms, 
     149                                          unsigned latency_ms, 
     150                                          unsigned options, 
     151                                          pjmedia_echo_state **p_echo ); 
    105152 
    106153/** 
     
    115162 
    116163/** 
    117  * Let the Echo Canceller knows that a frame has been played to the speaker. 
     164 * Reset the echo canceller. 
     165 * 
     166 * @param echo          The Echo Canceller. 
     167 * 
     168 * @return              PJ_SUCCESS on success. 
     169 */ 
     170PJ_DECL(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo ); 
     171 
     172 
     173/** 
     174 * Let the Echo Canceller know that a frame has been played to the speaker. 
    118175 * The Echo Canceller will keep the frame in its internal buffer, to be used 
    119176 * when cancelling the echo with #pjmedia_echo_capture(). 
     
    132189 
    133190/** 
    134  * Let the Echo Canceller knows that a frame has been captured from  
    135  * the microphone. 
    136  * The Echo Canceller will cancel the echo from the captured signal,  
    137  * using the internal buffer (supplied by #pjmedia_echo_playback())  
     191 * Let the Echo Canceller know that a frame has been captured from the  
     192 * microphone. The Echo Canceller will cancel the echo from the captured 
     193 * signal, using the internal buffer (supplied by #pjmedia_echo_playback()) 
    138194 * as the FES (Far End Speech) reference. 
    139195 * 
  • pjproject/trunk/pjmedia/src/pjmedia/echo_common.c

    r2039 r2198  
    1818 */ 
    1919 
    20 #include <pjmedia/config.h> 
    2120#include <pjmedia/echo.h> 
     21#include <pjmedia/delaybuf.h> 
     22#include <pjmedia/errno.h> 
    2223#include <pj/assert.h> 
     24#include <pj/list.h> 
     25#include <pj/log.h> 
    2326#include <pj/pool.h> 
    2427#include "echo_internal.h" 
    2528 
     29#define THIS_FILE   "echo_common.c" 
     30 
    2631typedef struct ec_operations ec_operations; 
    2732 
     33struct frame 
     34{ 
     35    PJ_DECL_LIST_MEMBER(struct frame); 
     36    short   buf[1]; 
     37}; 
     38 
    2839struct pjmedia_echo_state 
    2940{ 
     41    pj_pool_t       *pool; 
     42    char            *obj_name; 
     43    unsigned         samples_per_frame; 
    3044    void            *state; 
    3145    ec_operations   *op; 
     46 
     47    pj_bool_t        lat_ready;     /* lat_buf has been filled in.          */ 
     48    unsigned         lat_target_cnt;/* Target number of frames in lat_buf   */ 
     49    unsigned         lat_buf_cnt;   /* Actual number of frames in lat_buf   */ 
     50    struct frame     lat_buf;       /* Frame queue for delayed playback     */ 
     51    struct frame     lat_free;      /* Free frame list.                     */ 
     52 
     53    pjmedia_delay_buf   *delay_buf; 
    3254}; 
    3355 
     
    3557struct ec_operations 
    3658{ 
     59    const char *name; 
     60 
    3761    pj_status_t (*ec_create)(pj_pool_t *pool, 
    38                             unsigned clock_rate, 
    39                             unsigned samples_per_frame, 
    40                             unsigned tail_ms, 
    41                             unsigned latency_ms, 
    42                             unsigned options, 
    43                             void **p_state ); 
     62                             unsigned clock_rate, 
     63                             unsigned channel_count, 
     64                             unsigned samples_per_frame, 
     65                             unsigned tail_ms, 
     66                             unsigned options, 
     67                             void **p_state ); 
    4468    pj_status_t (*ec_destroy)(void *state ); 
    45     pj_status_t (*ec_playback)(void *state, 
    46                               pj_int16_t *play_frm ); 
    47     pj_status_t (*ec_capture)(void *state, 
    48                               pj_int16_t *rec_frm, 
    49                               unsigned options ); 
     69    void        (*ec_reset)(void *state ); 
    5070    pj_status_t (*ec_cancel)(void *state, 
    5171                             pj_int16_t *rec_frm, 
     
    5878static struct ec_operations echo_supp_op =  
    5979{ 
     80    "Echo suppressor", 
    6081    &echo_supp_create, 
    6182    &echo_supp_destroy, 
    62     &echo_supp_playback, 
    63     &echo_supp_capture, 
     83    &echo_supp_reset, 
    6484    &echo_supp_cancel_echo 
    6585}; 
     
    7191 */ 
    7292#if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0 
    73 static struct ec_operations aec_op =  
    74 { 
     93static struct ec_operations speex_aec_op =  
     94{ 
     95    "AEC", 
    7596    &speex_aec_create, 
    7697    &speex_aec_destroy, 
    77     &speex_aec_playback, 
    78     &speex_aec_capture, 
     98    &speex_aec_reset, 
    7999    &speex_aec_cancel_echo 
    80100}; 
    81  
    82 #else 
    83 #define aec_op echo_supp_op 
    84101#endif 
    85102 
    86103 
     104/* 
     105 * IPP AEC prototypes 
     106 */ 
     107#if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 
     108static struct ec_operations ipp_aec_op =  
     109{ 
     110    "IPP AEC", 
     111    &ipp_aec_create, 
     112    &ipp_aec_destroy, 
     113    &ipp_aec_reset, 
     114    &ipp_aec_cancel_echo 
     115}; 
     116#endif 
    87117 
    88118/* 
     
    97127                                         pjmedia_echo_state **p_echo ) 
    98128{ 
     129    return pjmedia_echo_create2(pool, clock_rate, 1, samples_per_frame, 
     130                                tail_ms, latency_ms, options, p_echo); 
     131} 
     132 
     133/* 
     134 * Create the echo canceller.  
     135 */ 
     136PJ_DEF(pj_status_t) pjmedia_echo_create2(pj_pool_t *pool, 
     137                                         unsigned clock_rate, 
     138                                         unsigned channel_count, 
     139                                         unsigned samples_per_frame, 
     140                                         unsigned tail_ms, 
     141                                         unsigned latency_ms, 
     142                                         unsigned options, 
     143                                         pjmedia_echo_state **p_echo ) 
     144{ 
     145    unsigned ptime; 
    99146    pjmedia_echo_state *ec; 
    100147    pj_status_t status; 
    101148 
    102     /* Force to use simple echo suppressor if AEC is not available */ 
    103 #if !defined(PJMEDIA_HAS_SPEEX_AEC) || PJMEDIA_HAS_SPEEX_AEC==0 
    104     options |= PJMEDIA_ECHO_SIMPLE; 
     149    /* Create new pool and instantiate and init the EC */ 
     150    pool = pj_pool_create(pool->factory, "ec%p", 256, 256, NULL); 
     151    ec = PJ_POOL_ZALLOC_T(pool, struct pjmedia_echo_state); 
     152    ec->pool = pool; 
     153    ec->obj_name = pool->obj_name; 
     154    pj_list_init(&ec->lat_buf); 
     155    pj_list_init(&ec->lat_free); 
     156 
     157    /* Select the backend algorithm */ 
     158    if (0) { 
     159        /* Dummy */ 
     160        ; 
     161#if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0 
     162    } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_SPEEX || 
     163               (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT)  
     164    { 
     165        ec->op = &speex_aec_op; 
    105166#endif 
    106167 
    107     ec = PJ_POOL_ZALLOC_T(pool, struct pjmedia_echo_state); 
    108  
    109     if (options & PJMEDIA_ECHO_SIMPLE) { 
     168#if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 
     169    } else if ((options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_IPP || 
     170               (options & PJMEDIA_ECHO_ALGO_MASK) == PJMEDIA_ECHO_DEFAULT) 
     171    { 
     172        ec->op = &ipp_aec_op; 
     173 
     174#endif 
     175 
     176    } else { 
    110177        ec->op = &echo_supp_op; 
    111         status = (*echo_supp_op.ec_create)(pool, clock_rate, samples_per_frame, 
    112                                            tail_ms, latency_ms, options, 
    113                                            &ec->state); 
     178    } 
     179 
     180    PJ_LOG(5,(ec->obj_name, "Creating %s", ec->op->name)); 
     181 
     182    /* Instantiate EC object */ 
     183    status = (*ec->op->ec_create)(pool, clock_rate, channel_count,  
     184                                  samples_per_frame, tail_ms,  
     185                                  options, &ec->state); 
     186    if (status != PJ_SUCCESS) { 
     187        pj_pool_release(pool); 
     188        return status; 
     189    } 
     190 
     191    /* Create latency buffers */ 
     192    ptime = samples_per_frame * 1000 / clock_rate; 
     193    if (latency_ms == 0) { 
     194        /* Give at least one frame delay to simplify programming */ 
     195        latency_ms = ptime; 
     196    } 
     197    ec->lat_target_cnt = latency_ms / ptime; 
     198    if (ec->lat_target_cnt != 0) { 
     199        unsigned i; 
     200        for (i=0; i < ec->lat_target_cnt; ++i)  { 
     201            struct frame *frm; 
     202 
     203            frm = (struct frame*) pj_pool_alloc(pool, (samples_per_frame<<1) + 
     204                                                      sizeof(struct frame)); 
     205            pj_list_push_back(&ec->lat_free, frm); 
     206        } 
    114207    } else { 
    115         ec->op = &aec_op; 
    116         status = (*aec_op.ec_create)(pool, clock_rate,  
    117                                      samples_per_frame, 
    118                                      tail_ms, latency_ms, options, 
    119                                      &ec->state); 
    120     } 
    121  
    122     if (status != PJ_SUCCESS) 
     208        ec->lat_ready = PJ_TRUE; 
     209    } 
     210 
     211    /* Create delay buffer to compensate drifts */ 
     212    status = pjmedia_delay_buf_create(ec->pool, ec->obj_name, clock_rate,  
     213                                      samples_per_frame, channel_count, 
     214                                      (PJMEDIA_SOUND_BUFFER_COUNT+1) * ptime, 
     215                                      0, &ec->delay_buf); 
     216    if (status != PJ_SUCCESS) { 
     217        pj_pool_release(pool); 
    123218        return status; 
    124  
    125     pj_assert(ec->state != NULL); 
    126  
     219    } 
     220 
     221    PJ_LOG(4,(ec->obj_name,  
     222              "%s created, clock_rate=%d, channel=%d, " 
     223              "samples per frame=%d, tail length=%d ms, " 
     224              "latency=%d ms",  
     225              ec->op->name, clock_rate, channel_count, samples_per_frame, 
     226              tail_ms, latency_ms)); 
     227 
     228    /* Done */ 
    127229    *p_echo = ec; 
    128230 
     
    136238PJ_DEF(pj_status_t) pjmedia_echo_destroy(pjmedia_echo_state *echo ) 
    137239{ 
    138     return (*echo->op->ec_destroy)(echo->state); 
    139 } 
    140  
    141  
    142  
    143 /* 
    144  * Let the Echo Canceller knows that a frame has been played to the speaker. 
     240    (*echo->op->ec_destroy)(echo->state); 
     241    pj_pool_release(echo->pool); 
     242    return PJ_SUCCESS; 
     243} 
     244 
     245 
     246/* 
     247 * Reset the echo canceller. 
     248 */ 
     249PJ_DEF(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo ) 
     250{ 
     251    while (!pj_list_empty(&echo->lat_buf)) { 
     252        struct frame *frm; 
     253        frm = echo->lat_buf.next; 
     254        pj_list_erase(frm); 
     255        pj_list_push_back(&echo->lat_free, frm); 
     256    } 
     257    echo->lat_ready = PJ_FALSE; 
     258    pjmedia_delay_buf_reset(echo->delay_buf); 
     259    echo->op->ec_reset(echo->state); 
     260    return PJ_SUCCESS; 
     261} 
     262 
     263 
     264/* 
     265 * Let the Echo Canceller know that a frame has been played to the speaker. 
    145266 */ 
    146267PJ_DEF(pj_status_t) pjmedia_echo_playback( pjmedia_echo_state *echo, 
    147268                                           pj_int16_t *play_frm ) 
    148269{ 
    149     return (*echo->op->ec_playback)(echo->state, play_frm); 
     270    if (!echo->lat_ready) { 
     271        /* We've not built enough latency in the buffer, so put this frame 
     272         * in the latency buffer list. 
     273         */ 
     274        struct frame *frm; 
     275 
     276        if (pj_list_empty(&echo->lat_free)) { 
     277            echo->lat_ready = PJ_TRUE; 
     278            PJ_LOG(5,(echo->obj_name, "Latency bufferring complete")); 
     279            pjmedia_delay_buf_put(echo->delay_buf, play_frm); 
     280            return PJ_SUCCESS; 
     281        } 
     282             
     283        frm = echo->lat_free.prev; 
     284        pj_list_erase(frm); 
     285 
     286        pjmedia_copy_samples(frm->buf, play_frm, echo->samples_per_frame); 
     287        pj_list_push_back(&echo->lat_buf, frm); 
     288 
     289    } else { 
     290        /* Latency buffer is ready (full), so we put this frame in the 
     291         * delay buffer. 
     292         */ 
     293        pjmedia_delay_buf_put(echo->delay_buf, play_frm); 
     294    } 
     295 
     296    return PJ_SUCCESS; 
    150297} 
    151298 
     
    159306                                          unsigned options ) 
    160307{ 
    161     return (*echo->op->ec_capture)(echo->state, rec_frm, options); 
     308    struct frame *oldest_frm; 
     309    pj_status_t status, rc; 
     310 
     311    if (!echo->lat_ready) { 
     312        /* Prefetching to fill in the desired latency */ 
     313        PJ_LOG(5,(echo->obj_name, "Prefetching..")); 
     314        return PJ_SUCCESS; 
     315    } 
     316 
     317    /* Retrieve oldest frame from the latency buffer */ 
     318    oldest_frm = echo->lat_buf.next; 
     319    pj_list_erase(oldest_frm); 
     320 
     321    /* Cancel echo using this reference frame */ 
     322    status = pjmedia_echo_cancel(echo, rec_frm, oldest_frm->buf,  
     323                                 options, NULL); 
     324 
     325    /* Move one frame from delay buffer to the latency buffer. */ 
     326    rc = pjmedia_delay_buf_get(echo->delay_buf, oldest_frm->buf); 
     327    if (rc != PJ_SUCCESS) { 
     328        /* Ooops.. no frame! */ 
     329        PJ_LOG(5,(echo->obj_name,  
     330                  "No frame from delay buffer. This will upset EC later")); 
     331        pjmedia_zero_samples(oldest_frm->buf, echo->samples_per_frame); 
     332    } 
     333    pj_list_push_back(&echo->lat_buf, oldest_frm); 
     334     
     335    return status; 
    162336} 
    163337 
  • pjproject/trunk/pjmedia/src/pjmedia/echo_internal.h

    r2039 r2198  
    2929PJ_DECL(pj_status_t) echo_supp_create(pj_pool_t *pool, 
    3030                                      unsigned clock_rate, 
     31                                      unsigned channel_count, 
    3132                                      unsigned samples_per_frame, 
    3233                                      unsigned tail_ms, 
    33                                       unsigned latency_ms, 
    3434                                      unsigned options, 
    3535                                      void **p_state ); 
    3636PJ_DECL(pj_status_t) echo_supp_destroy(void *state); 
    37 PJ_DECL(pj_status_t) echo_supp_playback(void *state, 
    38                                         pj_int16_t *play_frm ); 
    39 PJ_DECL(pj_status_t) echo_supp_capture(void *state, 
    40                                        pj_int16_t *rec_frm, 
    41                                        unsigned options ); 
     37PJ_DECL(void) echo_supp_reset(void *state); 
    4238PJ_DECL(pj_status_t) echo_supp_cancel_echo(void *state, 
    4339                                           pj_int16_t *rec_frm, 
     
    4844PJ_DECL(pj_status_t) speex_aec_create(pj_pool_t *pool, 
    4945                                      unsigned clock_rate, 
     46                                      unsigned channel_count, 
    5047                                      unsigned samples_per_frame, 
    5148                                      unsigned tail_ms, 
    52                                       unsigned latency_ms, 
    5349                                      unsigned options, 
    5450                                      void **p_state ); 
    5551PJ_DECL(pj_status_t) speex_aec_destroy(void *state ); 
    56 PJ_DECL(pj_status_t) speex_aec_playback(void *state, 
    57                                         pj_int16_t *play_frm ); 
    58 PJ_DECL(pj_status_t) speex_aec_capture(void *state, 
    59                                        pj_int16_t *rec_frm, 
    60                                        unsigned options ); 
     52PJ_DECL(void) speex_aec_reset(void *state ); 
    6153PJ_DECL(pj_status_t) speex_aec_cancel_echo(void *state, 
    6254                                           pj_int16_t *rec_frm, 
     
    6557                                           void *reserved ); 
    6658 
     59PJ_DECL(pj_status_t) ipp_aec_create(pj_pool_t *pool, 
     60                                    unsigned clock_rate, 
     61                                    unsigned channel_count, 
     62                                    unsigned samples_per_frame, 
     63                                    unsigned tail_ms, 
     64                                    unsigned options, 
     65                                    void **p_echo ); 
     66PJ_DECL(pj_status_t) ipp_aec_destroy(void *state ); 
     67PJ_DECL(void) ipp_aec_reset(void *state ); 
     68PJ_DECL(pj_status_t) ipp_aec_cancel_echo(void *state, 
     69                                         pj_int16_t *rec_frm, 
     70                                         const pj_int16_t *play_frm, 
     71                                         unsigned options, 
     72                                         void *reserved ); 
     73 
    6774 
    6875PJ_END_DECL 
  • pjproject/trunk/pjmedia/src/pjmedia/echo_port.c

    r2039 r2198  
    6868                           dn_port->info.samples_per_frame); 
    6969 
    70     status = pjmedia_echo_create(pool, dn_port->info.clock_rate,  
    71                                  dn_port->info.samples_per_frame, 
    72                                  tail_ms, latency_ms, options, &ec->ec); 
     70    status = pjmedia_echo_create2(pool, dn_port->info.clock_rate,  
     71                                  dn_port->info.channel_count, 
     72                                  dn_port->info.samples_per_frame, 
     73                                  tail_ms, latency_ms, options, &ec->ec); 
    7374    if (status != PJ_SUCCESS) 
    7475        return status; 
  • pjproject/trunk/pjmedia/src/pjmedia/echo_speex.c

    r2039 r2198  
    2020#include <pjmedia/echo.h> 
    2121#include <pjmedia/errno.h> 
    22 #include <pjmedia/silencedet.h> 
    2322#include <pj/assert.h> 
    24 #include <pj/lock.h> 
    25 #include <pj/log.h> 
    26 #include <pj/os.h> 
    2723#include <pj/pool.h> 
    2824#include <speex/speex_echo.h> 
     
    3026 
    3127#include "echo_internal.h" 
    32  
    33 #define THIS_FILE       "echo_speex.c" 
    34 #define BUF_COUNT       PJMEDIA_SOUND_BUFFER_COUNT 
    35 #define MIN_PREFETCH    2 
    36 #define MAX_PREFETCH    (BUF_COUNT*2/3) 
    37  
    38  
    39  
    40 #if 0 
    41 # define TRACE_(expr)  PJ_LOG(5,expr) 
    42 #else 
    43 # define TRACE_(expr) 
    44 #endif 
    45  
    46  
    47 typedef struct pjmedia_frame_queue pjmedia_frame_queue; 
    48  
    49 struct fq_frame 
    50 { 
    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_queue 
    58 { 
    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 enum 
    231 { 
    232     TS_FLAG_PLAY = 1, 
    233     TS_FLAG_REC  = 2, 
    234     TS_FLAG_OK   = 3, 
    235 }; 
    23628 
    23729typedef struct speex_ec 
     
    24436    unsigned              options; 
    24537    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  */ 
    25438} speex_ec; 
    25539 
     
    26145PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool, 
    26246                                     unsigned clock_rate, 
     47                                     unsigned channel_count, 
    26348                                     unsigned samples_per_frame, 
    26449                                     unsigned tail_ms, 
    265                                      unsigned latency_ms, 
    26650                                     unsigned options, 
    26751                                     void **p_echo ) 
     
    26953    speex_ec *echo; 
    27054    int sampling_rate; 
    271     pj_status_t status; 
    27255 
    27356    *p_echo = NULL; 
     
    27659    PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM); 
    27760 
    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  
    28861    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; 
    29462    echo->options = options; 
    29563 
    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 
    29873    if (echo->state == NULL) { 
    299         pj_lock_destroy(echo->lock); 
    30074        return PJ_ENOMEM; 
    30175    } 
     
    30680                   &sampling_rate); 
    30781 
    308     echo->preprocess = speex_preprocess_state_init(samples_per_frame,  
     82    echo->preprocess = speex_preprocess_state_init(echo->samples_per_frame, 
    30983                                                   clock_rate); 
    31084    if (echo->preprocess == NULL) { 
    31185        speex_echo_state_destroy(echo->state); 
    312         pj_lock_destroy(echo->lock); 
    31386        return PJ_ENOMEM; 
    31487    } 
     
    32598                         &disabled); 
    32699    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB,  
    327                          &disabled); 
     100                         &enabled); 
    328101#endif 
    329102 
     
    334107 
    335108    /* 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); 
    337110    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     } 
    355111 
    356112    /* Done */ 
    357113    *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)); 
    363114    return PJ_SUCCESS; 
    364115 
     
    375126    PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL); 
    376127 
    377     if (echo->lock) 
    378         pj_lock_acquire(echo->lock); 
    379  
    380128    if (echo->state) { 
    381129        speex_echo_state_destroy(echo->state); 
     
    388136    } 
    389137 
    390     if (echo->lock) { 
    391         pj_lock_destroy(echo->lock); 
    392         echo->lock = NULL; 
    393     } 
    394  
    395138    return PJ_SUCCESS; 
    396139} 
     
    398141 
    399142/* 
    400  * Let the AEC knows that a frame has been played to the speaker. 
     143 * Reset AEC 
    401144 */ 
    402 PJ_DEF(pj_status_t) speex_aec_playback(void *state, 
    403                                        pj_int16_t *play_frm ) 
     145PJ_DEF(void) speex_aec_reset(void *state ) 
    404146{ 
    405147    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); 
    521149} 
    522150 
  • pjproject/trunk/pjmedia/src/pjmedia/echo_suppress.c

    r2039 r2198  
    3636typedef struct echo_supp 
    3737{ 
    38     pj_bool_t            suppressing; 
    3938    pjmedia_silence_det *sd; 
    40     pj_time_val          last_signal; 
    4139    unsigned             samples_per_frame; 
    4240    unsigned             tail_ms; 
     
    5048PJ_DEF(pj_status_t) echo_supp_create( pj_pool_t *pool, 
    5149                                      unsigned clock_rate, 
     50                                      unsigned channel_count, 
    5251                                      unsigned samples_per_frame, 
    5352                                      unsigned tail_ms, 
    54                                       unsigned latency_ms, 
    5553                                      unsigned options, 
    5654                                      void **p_state ) 
     
    6058 
    6159    PJ_UNUSED_ARG(clock_rate); 
     60    PJ_UNUSED_ARG(channel_count); 
    6261    PJ_UNUSED_ARG(options); 
    63     PJ_UNUSED_ARG(latency_ms); 
    6462 
    6563    ec = PJ_POOL_ZALLOC_T(pool, struct echo_supp); 
     
    9290 
    9391/* 
    94  * Let the AEC knows that a frame has been played to the speaker. 
     92 * Reset 
    9593 */ 
    96 PJ_DEF(pj_status_t) echo_supp_playback( void *state, 
    97                                         pj_int16_t *play_frm ) 
     94PJ_DEF(void) echo_supp_reset(void *state) 
    9895{ 
    99     echo_supp *ec = (echo_supp*) state; 
    100     pj_bool_t silence; 
    101     pj_bool_t last_suppressing = ec->suppressing; 
    102  
    103     silence = pjmedia_silence_det_detect(ec->sd, play_frm, 
    104                                          ec->samples_per_frame, NULL); 
    105  
    106     ec->suppressing = !silence; 
    107  
    108     if (ec->suppressing) { 
    109         pj_gettimeofday(&ec->last_signal); 
    110     } 
    111  
    112     if (ec->suppressing!=0 && last_suppressing==0) { 
    113         PJ_LOG(5,(THIS_FILE, "Start suppressing..")); 
    114     } else if (ec->suppressing==0 && last_suppressing!=0) { 
    115         PJ_LOG(5,(THIS_FILE, "Stop suppressing..")); 
    116     } 
    117  
    118     return PJ_SUCCESS; 
     96    PJ_UNUSED_ARG(state); 
     97    return; 
    11998} 
    120  
    121  
    122 /* 
    123  * Let the AEC knows that a frame has been captured from the microphone. 
    124  */ 
    125 PJ_DEF(pj_status_t) echo_supp_capture( void *state, 
    126                                        pj_int16_t *rec_frm, 
    127                                        unsigned options ) 
    128 { 
    129     echo_supp *ec = (echo_supp*) state; 
    130     pj_time_val now; 
    131     unsigned delay_ms; 
    132  
    133     PJ_UNUSED_ARG(options); 
    134  
    135     pj_gettimeofday(&now); 
    136  
    137     PJ_TIME_VAL_SUB(now, ec->last_signal); 
    138     delay_ms = PJ_TIME_VAL_MSEC(now); 
    139  
    140     if (delay_ms < ec->tail_ms) { 
    141 #if defined(PJMEDIA_ECHO_SUPPRESS_FACTOR) && PJMEDIA_ECHO_SUPPRESS_FACTOR!=0 
    142         unsigned i; 
    143         for (i=0; i<ec->samples_per_frame; ++i) { 
    144             rec_frm[i] = (pj_int16_t)(rec_frm[i] >>  
    145                                       PJMEDIA_ECHO_SUPPRESS_FACTOR); 
    146         } 
    147 #else 
    148         pjmedia_zero_samples(rec_frm, ec->samples_per_frame); 
    149 #endif 
    150     } 
    151  
    152     return PJ_SUCCESS; 
    153 } 
    154  
    15599 
    156100/* 
  • pjproject/trunk/pjmedia/src/pjmedia/sound_port.c

    r2039 r2198  
    545545            si.rec_latency = si.play_latency = 0; 
    546546 
    547         delay_ms = (si.rec_latency + si.play_latency) * 1000 / 
    548                    snd_port->clock_rate; 
    549         status = pjmedia_echo_create(pool, snd_port->clock_rate,  
    550                                     snd_port->samples_per_frame,  
    551                                     tail_ms, delay_ms, 
    552                                     options, &snd_port->ec_state); 
     547        //No need to add input latency in the latency calculation, 
     548        //since actual input latency should be zero. 
     549        //delay_ms = (si.rec_latency + si.play_latency) * 1000 / 
     550        //         snd_port->clock_rate; 
     551        delay_ms = si.play_latency * 1000 / snd_port->clock_rate; 
     552        status = pjmedia_echo_create2(pool, snd_port->clock_rate,  
     553                                      snd_port->channel_count, 
     554                                      snd_port->samples_per_frame,  
     555                                      tail_ms, delay_ms, 
     556                                      options, &snd_port->ec_state); 
    553557        if (status != PJ_SUCCESS) 
    554558            snd_port->ec_state = NULL; 
  • pjproject/trunk/pjsip-apps/src/samples/aectest.c

    r2039 r2198  
    3333#include <pjlib.h> 
    3434 
    35 /* For logging purpose. */ 
    36 #define THIS_FILE   "playfile.c" 
     35#define THIS_FILE   "aectest.c" 
    3736#define PTIME       20 
    38 #define TAIL_LENGTH 800 
     37#define TAIL_LENGTH 200 
    3938 
    4039static const char *desc =  
     
    4948" USAGE                                                             \n" 
    5049"                                                                   \n" 
    51 "  aectest INPUT.WAV OUTPUT.WAV                                     \n" 
    52 "                                                                   \n" 
    53 "  INPUT.WAV is the file to be played to the speaker.               \n" 
    54 "  OUTPUT.WAV is the output file containing recorded signal from the\n" 
    55 "  microphone."; 
    56  
     50"  aectest [options] <PLAY.WAV> <REC.WAV> <OUTPUT.WAV>              \n" 
     51"                                                                   \n" 
     52"  <PLAY.WAV>   is the signal played to the speaker.                \n" 
     53"  <REC.WAV>    is the signal captured from the microphone.         \n" 
     54"  <OUTPUT.WAV> is the output file to store the test result         \n" 
     55"\n" 
     56" options:\n" 
     57"  -d  The delay between playback and capture in ms. Default is zero.\n" 
     58"  -l  Set the echo tail length in ms. Default is 200 ms            \n" 
     59"  -a  Algorithm: 0=default, 1=speex, 3=echo suppress               \n"; 
     60 
     61/*  
     62 * Sample session: 
     63 * 
     64 * -d 100 -a 1 ../bin/orig8.wav ../bin/echo8.wav ../bin/result8.wav  
     65 */ 
    5766 
    5867static void app_perror(const char *sender, const char *title, pj_status_t st) 
     
    7382    pjmedia_endpt *med_endpt; 
    7483    pj_pool_t     *pool; 
    75     pjmedia_port  *play_port; 
    76     pjmedia_port  *rec_port; 
    77     pjmedia_port  *bidir_port; 
    78     pjmedia_snd_port *snd; 
    79     char tmp[10]; 
     84    pjmedia_port  *wav_play; 
     85    pjmedia_port  *wav_rec; 
     86    pjmedia_port  *wav_out; 
    8087    pj_status_t status; 
    81  
    82  
    83     if (argc != 3) { 
    84         puts("Error: arguments required"); 
     88    pjmedia_echo_state *ec; 
     89    pjmedia_frame play_frame, rec_frame; 
     90    unsigned opt = 0; 
     91    unsigned latency_ms = 0; 
     92    unsigned tail_ms = TAIL_LENGTH; 
     93    pj_timestamp t0, t1; 
     94    int c; 
     95 
     96    pj_optind = 0; 
     97    while ((c=pj_getopt(argc, argv, "d:l:a:")) !=-1) { 
     98        switch (c) { 
     99        case 'd': 
     100            latency_ms = atoi(pj_optarg); 
     101            break; 
     102        case 'l': 
     103            tail_ms = atoi(pj_optarg); 
     104            break; 
     105        case 'a': 
     106            { 
     107                int alg = atoi(pj_optarg); 
     108                switch (alg) { 
     109                case 0: 
     110                    opt = 0; 
     111                case 1: 
     112                    opt = PJMEDIA_ECHO_SPEEX; 
     113                    break; 
     114                case 3: 
     115                    opt = PJMEDIA_ECHO_SIMPLE; 
     116                    break; 
     117                default: 
     118                    puts("Invalid algorithm"); 
     119                    puts(desc); 
     120                    return 1; 
     121                } 
     122            } 
     123            break; 
     124        } 
     125    } 
     126 
     127    if (argc - pj_optind != 3) { 
     128        puts("Error: missing argument(s)"); 
    85129        puts(desc); 
    86130        return 1; 
    87131    } 
    88  
    89132 
    90133    /* Must init PJLIB first: */ 
     
    110153                           ); 
    111154 
    112     /* Create file media port from the WAV file */ 
    113     status = pjmedia_wav_player_port_create(  pool,     /* memory pool      */ 
    114                                               argv[1],  /* file to play     */ 
    115                                               PTIME,    /* ptime.           */ 
    116                                               0,        /* flags            */ 
    117                                               0,        /* default buffer   */ 
    118                                               &play_port); 
    119     if (status != PJ_SUCCESS) { 
    120         app_perror(THIS_FILE, "Unable to open input WAV file", status); 
    121         return 1; 
    122     } 
    123  
    124     if (play_port->info.channel_count != 1) { 
    125         puts("Error: input WAV must have 1 channel audio"); 
    126         return 1; 
    127     } 
    128     if (play_port->info.bits_per_sample != 16) { 
    129         puts("Error: input WAV must be encoded as 16bit PCM"); 
    130         return 1; 
    131     } 
    132  
    133 #ifdef PJ_DARWINOS 
    134     /* Need to force clock rate on MacOS */ 
    135     if (play_port->info.clock_rate != 44100) { 
    136         pjmedia_port *resample_port; 
    137  
    138         status = pjmedia_resample_port_create(pool, play_port, 44100, 0, 
    139                                               &resample_port); 
    140         if (status != PJ_SUCCESS) { 
    141             app_perror(THIS_FILE, "Unable to create resampling port", status); 
    142             return 1; 
    143         } 
    144  
    145         data.play_port = resample_port; 
    146     } 
    147 #endif 
    148  
    149     /* Create WAV output file port */ 
    150     status = pjmedia_wav_writer_port_create(pool, argv[2],  
    151                                             play_port->info.clock_rate, 
    152                                             play_port->info.channel_count, 
    153                                             play_port->info.samples_per_frame, 
    154                                             play_port->info.bits_per_sample, 
    155                                             0, 0, &rec_port); 
    156     if (status != PJ_SUCCESS) { 
    157         app_perror(THIS_FILE, "Unable to open output file", status); 
    158         return 1; 
    159     } 
    160  
    161     /* Create bidirectional port from the WAV ports */ 
    162     pjmedia_bidirectional_port_create(pool, play_port, rec_port, &bidir_port); 
    163  
    164     /* Create sound device. */ 
    165     status = pjmedia_snd_port_create(pool, -1, -1,  
    166                                      play_port->info.clock_rate, 
    167                                      play_port->info.channel_count, 
    168                                      play_port->info.samples_per_frame, 
    169                                      play_port->info.bits_per_sample, 
    170                                      0, &snd); 
    171     if (status != PJ_SUCCESS) { 
    172         app_perror(THIS_FILE, "Unable to open sound device", status); 
    173         return 1; 
    174     } 
    175  
    176  
    177     /* Customize AEC */ 
    178     pjmedia_snd_port_set_ec(snd, pool, TAIL_LENGTH, 0); 
    179  
    180     /* Connect sound to the port */ 
    181     pjmedia_snd_port_connect(snd, bidir_port); 
    182  
    183  
    184     puts(""); 
    185     printf("Playing %s and recording to %s\n", argv[1], argv[2]); 
    186     puts("Press <ENTER> to quit"); 
    187  
    188     fgets(tmp, sizeof(tmp), stdin); 
    189  
     155    /* Open wav_play */ 
     156    status = pjmedia_wav_player_port_create(pool, argv[pj_optind], PTIME,  
     157                                            PJMEDIA_FILE_NO_LOOP, 0,  
     158                                            &wav_play); 
     159    if (status != PJ_SUCCESS) { 
     160        app_perror(THIS_FILE, "Error opening playback WAV file", status); 
     161        return 1; 
     162    } 
    190163     
    191     /* Start deinitialization: */ 
    192  
    193     /* Destroy sound device */ 
    194     status = pjmedia_snd_port_destroy( snd ); 
    195     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 
    196  
     164    /* Open recorded wav */ 
     165    status = pjmedia_wav_player_port_create(pool, argv[pj_optind+1], PTIME,  
     166                                            PJMEDIA_FILE_NO_LOOP, 0,  
     167                                            &wav_rec); 
     168    if (status != PJ_SUCCESS) { 
     169        app_perror(THIS_FILE, "Error opening recorded WAV file", status); 
     170        return 1; 
     171    } 
     172 
     173    /* play and rec WAVs must have the same clock rate */ 
     174    if (wav_play->info.clock_rate != wav_rec->info.clock_rate) { 
     175        puts("Error: clock rate mismatch in the WAV files"); 
     176        return 1; 
     177    } 
     178 
     179    /* .. and channel count */ 
     180    if (wav_play->info.channel_count != wav_rec->info.channel_count) { 
     181        puts("Error: clock rate mismatch in the WAV files"); 
     182        return 1; 
     183    } 
     184 
     185    /* Create output wav */ 
     186    status = pjmedia_wav_writer_port_create(pool, argv[pj_optind+2], 
     187                                            wav_play->info.clock_rate, 
     188                                            wav_play->info.channel_count, 
     189                                            wav_play->info.samples_per_frame, 
     190                                            wav_play->info.bits_per_sample, 
     191                                            0, 0, &wav_out); 
     192    if (status != PJ_SUCCESS) { 
     193        app_perror(THIS_FILE, "Error opening output WAV file", status); 
     194        return 1; 
     195    } 
     196 
     197    /* Create echo canceller */ 
     198    status = pjmedia_echo_create2(pool, wav_play->info.clock_rate, 
     199                                  wav_play->info.channel_count, 
     200                                  wav_play->info.samples_per_frame, 
     201                                  tail_ms, latency_ms, 
     202                                  opt, &ec); 
     203    if (status != PJ_SUCCESS) { 
     204        app_perror(THIS_FILE, "Error creating EC", status); 
     205        return 1; 
     206    } 
     207 
     208 
     209    /* Processing loop */ 
     210    play_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1); 
     211    rec_frame.buf = pj_pool_alloc(pool, wav_play->info.samples_per_frame<<1); 
     212    pj_get_timestamp(&t0); 
     213    for (;;) { 
     214        play_frame.size = wav_play->info.samples_per_frame << 1; 
     215        status = pjmedia_port_get_frame(wav_play, &play_frame); 
     216        if (status != PJ_SUCCESS) 
     217            break; 
     218 
     219        status = pjmedia_echo_playback(ec, (short*)play_frame.buf); 
     220 
     221        rec_frame.size = wav_play->info.samples_per_frame << 1; 
     222        status = pjmedia_port_get_frame(wav_rec, &rec_frame); 
     223        if (status != PJ_SUCCESS) 
     224            break; 
     225 
     226        status = pjmedia_echo_capture(ec, (short*)rec_frame.buf, 0); 
     227 
     228        //status = pjmedia_echo_cancel(ec, (short*)rec_frame.buf,  
     229        //                           (short*)play_frame.buf, 0, NULL); 
     230 
     231        pjmedia_port_put_frame(wav_out, &rec_frame); 
     232    } 
     233    pj_get_timestamp(&t1); 
     234 
     235    PJ_LOG(3,(THIS_FILE, "Completed in %u msec\n", pj_elapsed_msec(&t0, &t1))); 
    197236 
    198237    /* Destroy file port(s) */ 
    199     status = pjmedia_port_destroy( play_port ); 
    200     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 
    201     status = pjmedia_port_destroy( rec_port ); 
    202     PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 
    203  
     238    status = pjmedia_port_destroy( wav_play ); 
     239    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 
     240    status = pjmedia_port_destroy( wav_rec ); 
     241    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 
     242    status = pjmedia_port_destroy( wav_out ); 
     243    PJ_ASSERT_RETURN(status == PJ_SUCCESS, 1); 
     244 
     245    /* Destroy ec */ 
     246    pjmedia_echo_destroy(ec); 
    204247 
    205248    /* Release application pool */ 
  • pjproject/trunk/third_party/speex/libspeex/mdf.c

    r2184 r2198  
    1 /* Copyright (C) 2003-2008 Jean-Marc Valin 
     1/* Copyright (C) 2003-2006 Jean-Marc Valin 
    22 
    33   File: mdf.c 
     
    8989#endif 
    9090 
    91 #ifdef FIXED_POINT 
    92 #define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))   
    93 #else 
    94 #define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))   
    95 #endif 
    96  
    9791/* If enabled, the AEC will use a foreground filter and a background filter to be more robust to double-talk 
    9892   and difficult signals in general. The cost is an extra FFT and a matrix-vector multiply */ 
     
    138132   int saturated; 
    139133   int screwed_up; 
    140    int C;                    /** Number of input channels (microphones) */ 
    141    int K;                    /** Number of output channels (loudspeakers) */ 
    142134   spx_int32_t sampling_rate; 
    143135   spx_word16_t spec_average; 
     
    180172   spx_word16_t *prop; 
    181173   void *fft_table; 
    182    spx_word16_t *memX, *memD, *memE; 
     174   spx_word16_t memX, memD, memE; 
    183175   spx_word16_t preemph; 
    184176   spx_word16_t notch_radius; 
    185    spx_mem_t *notch_mem; 
     177   spx_mem_t notch_mem[2]; 
    186178 
    187179   /* NOTE: If you only use speex_echo_cancel() and want to save some memory, remove this */ 
     
    191183}; 
    192184 
    193 static inline void filter_dc_notch16(const spx_int16_t *in, spx_word16_t radius, spx_word16_t *out, int len, spx_mem_t *mem, int stride) 
     185static inline void filter_dc_notch16(const spx_int16_t *in, spx_word16_t radius, spx_word16_t *out, int len, spx_mem_t *mem) 
    194186{ 
    195187   int i; 
     
    203195   for (i=0;i<len;i++) 
    204196   { 
    205       spx_word16_t vin = in[i*stride]; 
     197      spx_word16_t vin = in[i]; 
    206198      spx_word32_t vout = mem[0] + SHL32(EXTEND32(vin),15); 
    207199#ifdef FIXED_POINT 
     
    243235} 
    244236 
    245 /** Compute power spectrum of a half-complex (packed) vector and accumulate */ 
    246 static inline void power_spectrum_accum(const spx_word16_t *X, spx_word32_t *ps, int N) 
    247 { 
    248    int i, j; 
    249    ps[0]+=MULT16_16(X[0],X[0]); 
    250    for (i=1,j=1;i<N-1;i+=2,j++) 
    251    { 
    252       ps[j] +=  MULT16_16(X[i],X[i]) + MULT16_16(X[i+1],X[i+1]); 
    253    } 
    254    ps[j]+=MULT16_16(X[i],X[i]); 
    255 } 
    256  
    257237/** Compute cross-power spectrum of a half-complex (packed) vectors and add to acc */ 
    258238#ifdef FIXED_POINT 
     
    351331} 
    352332 
    353 static inline void mdf_adjust_prop(const spx_word32_t *W, int N, int M, int P, spx_word16_t *prop) 
    354 { 
    355    int i, j, p; 
     333static inline void mdf_adjust_prop(const spx_word32_t *W, int N, int M, spx_word16_t *prop) 
     334{ 
     335   int i, j; 
    356336   spx_word16_t max_sum = 1; 
    357337   spx_word32_t prop_sum = 1; 
     
    359339   { 
    360340      spx_word32_t tmp = 1; 
    361       for (p=0;p<P;p++) 
    362          for (j=0;j<N;j++) 
    363             tmp += MULT16_16(EXTRACT16(SHR32(W[p*N*M + i*N+j],18)), EXTRACT16(SHR32(W[p*N*M + i*N+j],18))); 
     341      for (j=0;j<N;j++) 
     342         tmp += MULT16_16(EXTRACT16(SHR32(W[i*N+j],18)), EXTRACT16(SHR32(W[i*N+j],18))); 
    364343#ifdef FIXED_POINT 
    365344      /* Just a security in case an overflow were to occur */ 
     
    400379 
    401380/** Creates a new echo canceller state */ 
    402 EXPORT SpeexEchoState *speex_echo_state_init(int frame_size, int filter_length) 
    403 { 
    404    return speex_echo_state_init_mc(frame_size, filter_length, 1, 1); 
    405 } 
    406  
    407 EXPORT SpeexEchoState *speex_echo_state_init_mc(int frame_size, int filter_length, int nb_mic, int nb_speakers) 
    408 { 
    409    int i,N,M, C, K; 
     381SpeexEchoState *speex_echo_state_init(int frame_size, int filter_length) 
     382{ 
     383   int i,N,M; 
    410384   SpeexEchoState *st = (SpeexEchoState *)speex_alloc(sizeof(SpeexEchoState)); 
    411385 
    412    st->K = nb_speakers; 
    413    st->C = nb_mic; 
    414    C=st->C; 
    415    K=st->K; 
    416386#ifdef DUMP_ECHO_CANCEL_DATA 
    417387   if (rFile || pFile || oFile) 
     
    444414   st->fft_table = spx_fft_init(N); 
    445415    
    446    st->e = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t)); 
    447    st->x = (spx_word16_t*)speex_alloc(K*N*sizeof(spx_word16_t)); 
    448    st->input = (spx_word16_t*)speex_alloc(C*st->frame_size*sizeof(spx_word16_t)); 
    449    st->y = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t)); 
    450    st->last_y = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t)); 
     416   st->e = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); 
     417   st->x = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); 
     418   st->input = (spx_word16_t*)speex_alloc(st->frame_size*sizeof(spx_word16_t)); 
     419   st->y = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); 
     420   st->last_y = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); 
    451421   st->Yf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); 
    452422   st->Rf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); 
     
    455425   st->Eh = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); 
    456426 
    457    st->X = (spx_word16_t*)speex_alloc(K*(M+1)*N*sizeof(spx_word16_t)); 
    458    st->Y = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t)); 
    459    st->E = (spx_word16_t*)speex_alloc(C*N*sizeof(spx_word16_t)); 
    460    st->W = (spx_word32_t*)speex_alloc(C*K*M*N*sizeof(spx_word32_t)); 
     427   st->X = (spx_word16_t*)speex_alloc((M+1)*N*sizeof(spx_word16_t)); 
     428   st->Y = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); 
     429   st->E = (spx_word16_t*)speex_alloc(N*sizeof(spx_word16_t)); 
     430   st->W = (spx_word32_t*)speex_alloc(M*N*sizeof(spx_word32_t)); 
    461431#ifdef TWO_PATH 
    462    st->foreground = (spx_word16_t*)speex_alloc(M*N*C*K*sizeof(spx_word16_t)); 
     432   st->foreground = (spx_word16_t*)speex_alloc(M*N*sizeof(spx_word16_t)); 
    463433#endif 
    464434   st->PHI = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t)); 
     
    481451   for (i=0;i<=st->frame_size;i++) 
    482452      st->power_1[i] = FLOAT_ONE; 
    483    for (i=0;i<N*M*K*C;i++) 
     453   for (i=0;i<N*M;i++) 
    484454      st->W[i] = 0; 
    485455   { 
     
    496466      for (i=M-1;i>=0;i--) 
    497467      { 
    498          st->prop[i] = DIV32(MULT16_16(QCONST16(.8f,15), st->prop[i]),sum); 
    499       } 
    500    } 
    501     
    502    st->memX = (spx_word16_t*)speex_alloc(K*sizeof(spx_word16_t)); 
    503    st->memD = (spx_word16_t*)speex_alloc(C*sizeof(spx_word16_t)); 
    504    st->memE = (spx_word16_t*)speex_alloc(C*sizeof(spx_word16_t)); 
     468         st->prop[i] = DIV32(MULT16_16(QCONST16(.8,15), st->prop[i]),sum); 
     469      } 
     470   } 
     471    
     472   st->memX=st->memD=st->memE=0; 
    505473   st->preemph = QCONST16(.9,15); 
    506474   if (st->sampling_rate<12000) 
     
    511479      st->notch_radius = QCONST16(.992, 15); 
    512480 
    513    st->notch_mem = (spx_mem_t*)speex_alloc(2*C*sizeof(spx_mem_t)); 
     481   st->notch_mem[0] = st->notch_mem[1] = 0; 
    514482   st->adapted = 0; 
    515483   st->Pey = st->Pyy = FLOAT_ONE; 
     
    520488#endif 
    521489    
    522    st->play_buf = (spx_int16_t*)speex_alloc(K*(PLAYBACK_DELAY+1)*st->frame_size*sizeof(spx_int16_t)); 
     490   st->play_buf = (spx_int16_t*)speex_alloc((PLAYBACK_DELAY+1)*st->frame_size*sizeof(spx_int16_t)); 
    523491   st->play_buf_pos = PLAYBACK_DELAY*st->frame_size; 
    524492   st->play_buf_started = 0; 
     
    528496 
    529497/** Resets echo canceller state */ 
    530 EXPORT void speex_echo_state_reset(SpeexEchoState *st) 
    531 { 
    532    int i, M, N, C, K; 
     498void speex_echo_state_reset(SpeexEchoState *st) 
     499{ 
     500   int i, M, N; 
    533501   st->cancel_count=0; 
    534502   st->screwed_up = 0; 
    535503   N = st->window_size; 
    536504   M = st->M; 
    537    C=st->C; 
    538    K=st->K; 
    539505   for (i=0;i<N*M;i++) 
    540506      st->W[i] = 0; 
     
    556522      st->last_y[i] = 0; 
    557523   } 
    558    for (i=0;i<N*C;i++) 
     524   for (i=0;i<N;i++) 
    559525   { 
    560526      st->E[i] = 0; 
    561    } 
    562    for (i=0;i<N*K;i++) 
    563    { 
    564527      st->x[i] = 0; 
    565528   } 
    566    for (i=0;i<2*C;i++) 
    567       st->notch_mem[i] = 0; 
    568    for (i=0;i<C;i++) 
    569       st->memD[i]=st->memE[i]=0; 
    570    for (i=0;i<K;i++) 
    571       st->memX[i]=0; 
     529   st->notch_mem[0] = st->notch_mem[1] = 0; 
     530   st->memX=st->memD=st->memE=0; 
    572531 
    573532   st->saturated = 0; 
     
    587546 
    588547/** Destroys an echo canceller state */ 
    589 EXPORT void speex_echo_state_destroy(SpeexEchoState *st) 
     548void speex_echo_state_destroy(SpeexEchoState *st) 
    590549{ 
    591550   spx_fft_destroy(st->fft_table); 
     
    618577   speex_free(st->wtmp2); 
    619578#endif 
    620    speex_free(st->memX); 
    621    speex_free(st->memD); 
    622    speex_free(st->memE); 
    623    speex_free(st->notch_mem); 
    624  
    625579   speex_free(st->play_buf); 
    626580   speex_free(st); 
     
    634588} 
    635589 
    636 EXPORT void speex_echo_capture(SpeexEchoState *st, const spx_int16_t *rec, spx_int16_t *out) 
     590void speex_echo_capture(SpeexEchoState *st, const spx_int16_t *rec, spx_int16_t *out) 
    637591{ 
    638592   int i; 
     
    657611} 
    658612 
    659 EXPORT void speex_echo_playback(SpeexEchoState *st, const spx_int16_t *play) 
     613void speex_echo_playback(SpeexEchoState *st, const spx_int16_t *play) 
    660614{ 
    661615   /*speex_warning_int("playback with fill level ", st->play_buf_pos/st->frame_size);*/ 
     
    684638 
    685639/** Performs echo cancellation on a frame (deprecated, last arg now ignored) */ 
    686 EXPORT void speex_echo_cancel(SpeexEchoState *st, const spx_int16_t *in, const spx_int16_t *far_end, spx_int16_t *out, spx_int32_t *Yout) 
     640void speex_echo_cancel(SpeexEchoState *st, const spx_int16_t *in, const spx_int16_t *far_end, spx_int16_t *out, spx_int32_t *Yout) 
    687641{ 
    688642   speex_echo_cancellation(st, in, far_end, out); 
     
    690644 
    691645/** Performs echo cancellation on a frame */ 
    692 EXPORT void speex_echo_cancellation(SpeexEchoState *st, const spx_int16_t *in, const spx_int16_t *far_end, spx_int16_t *out) 
    693 { 
    694    int i,j, chan, speak; 
    695    int N,M, C, K; 
     646void speex_echo_cancellation(SpeexEchoState *st, const spx_int16_t *in, const spx_int16_t *far_end, spx_int16_t *out) 
     647{ 
     648   int i,j; 
     649   int N,M; 
    696650   spx_word32_t Syy,See,Sxx,Sdd, Sff; 
    697651#ifdef TWO_PATH 
     
    708662   N = st->window_size; 
    709663   M = st->M; 
    710    C = st->C; 
    711    K = st->K; 
    712  
    713664   st->cancel_count++; 
    714665#ifdef FIXED_POINT 
     
    720671#endif 
    721672 
    722    for (chan = 0; chan < C; chan++) 
    723    { 
    724       /* Apply a notch filter to make sure DC doesn't end up causing problems */ 
    725       filter_dc_notch16(in+chan, st->notch_radius, st->input+chan*st->frame_size, st->frame_size, st->notch_mem+2*chan, C); 
    726       /* Copy input data to buffer and apply pre-emphasis */ 
    727       /* Copy input data to buffer */ 
    728       for (i=0;i<st->frame_size;i++) 
    729       { 
    730          spx_word32_t tmp32; 
    731          /* FIXME: This core has changed a bit, need to merge properly */ 
    732          tmp32 = SUB32(EXTEND32(st->input[chan*st->frame_size+i]), EXTEND32(MULT16_16_P15(st->preemph, st->memD[chan]))); 
    733 #ifdef FIXED_POINT 
    734          if (tmp32 > 32767) 
    735          { 
    736             tmp32 = 32767; 
    737             if (st->saturated == 0) 
    738                st->saturated = 1; 
    739          }       
    740          if (tmp32 < -32767) 
    741          { 
    742             tmp32 = -32767; 
    743             if (st->saturated == 0) 
    744                st->saturated = 1; 
    745          } 
    746 #endif 
    747          st->memD[chan] = st->input[chan*st->frame_size+i]; 
    748          st->input[chan*st->frame_size+i] = EXTRACT16(tmp32); 
    749       } 
    750    } 
    751  
    752    for (speak = 0; speak < K; speak++) 
    753    { 
    754       for (i=0;i<st->frame_size;i++) 
    755       { 
    756          spx_word32_t tmp32; 
    757          st->x[speak*N+i] = st->x[speak*N+i+st->frame_size]; 
    758          tmp32 = SUB32(EXTEND32(far_end[i*K+speak]), EXTEND32(MULT16_16_P15(st->preemph, st->memX[speak]))); 
    759 #ifdef FIXED_POINT 
    760          /*FIXME: If saturation occurs here, we need to freeze adaptation for M frames (not just one) */ 
    761          if (tmp32 > 32767) 
    762          { 
    763             tmp32 = 32767; 
    764             st->saturated = M+1; 
    765          }       
    766          if (tmp32 < -32767) 
    767          { 
    768             tmp32 = -32767; 
    769             st->saturated = M+1; 
    770          }       
    771 #endif 
    772          st->x[speak*N+i+st->frame_size] = EXTRACT16(tmp32); 
    773          st->memX[speak] = far_end[i*K+speak]; 
    774       } 
    775    }    
    776     
    777    for (speak = 0; speak < K; speak++) 
    778    { 
    779       /* Shift memory: this could be optimized eventually*/ 
    780       for (j=M-1;j>=0;j--) 
    781       { 
    782          for (i=0;i<N;i++) 
    783             st->X[(j+1)*N*K+speak*N+i] = st->X[j*N*K+speak*N+i]; 
    784       } 
    785       /* Convert x (echo input) to frequency domain */ 
    786       spx_fft(st->fft_table, st->x+speak*N, &st->X[speak*N]); 
    787    } 
    788     
    789    Sxx = 0; 
    790    for (speak = 0; speak < K; speak++) 
    791    { 
    792       Sxx += mdf_inner_prod(st->x+speak*N+st->frame_size, st->x+speak*N+st->frame_size, st->frame_size); 
    793       power_spectrum_accum(st->X+speak*N, st->Xf, N); 
    794    } 
    795     
    796    Sff = 0;   
    797    for (chan = 0; chan < C; chan++) 
    798    { 
     673   /* Apply a notch filter to make sure DC doesn't end up causing problems */ 
     674   filter_dc_notch16(in, st->notch_radius, st->input, st->frame_size, st->notch_mem); 
     675   /* Copy input data to buffer and apply pre-emphasis */ 
     676   for (i=0;i<st->frame_size;i++) 
     677   { 
     678      spx_word32_t tmp32; 
     679      tmp32 = SUB32(EXTEND32(far_end[i]), EXTEND32(MULT16_16_P15(st->preemph, st->memX))); 
     680#ifdef FIXED_POINT 
     681      /* If saturation occurs here, we need to freeze adaptation for M+1 frames (not just one) */ 
     682      if (tmp32 > 32767) 
     683      { 
     684         tmp32 = 32767; 
     685         st->saturated = M+1; 
     686      } 
     687      if (tmp32 < -32767) 
     688      { 
     689         tmp32 = -32767; 
     690         st->saturated = M+1; 
     691      }       
     692#endif 
     693      st->x[i+st->frame_size] = EXTRACT16(tmp32); 
     694      st->memX = far_end[i]; 
     695       
     696      tmp32 = SUB32(EXTEND32(st->input[i]), EXTEND32(MULT16_16_P15(st->preemph, st->memD))); 
     697#ifdef FIXED_POINT 
     698      if (tmp32 > 32767) 
     699      { 
     700         tmp32 = 32767; 
     701         if (st->saturated == 0) 
     702            st->saturated = 1; 
     703      }       
     704      if (tmp32 < -32767) 
     705      { 
     706         tmp32 = -32767; 
     707         if (st->saturated == 0) 
     708            st->saturated = 1; 
     709      } 
     710#endif 
     711      st->memD = st->input[i]; 
     712      st->input[i] = tmp32; 
     713   } 
     714 
     715   /* Shift memory: this could be optimized eventually*/ 
     716   for (j=M-1;j>=0;j--) 
     717   { 
     718      for (i=0;i<N;i++) 
     719         st->X[(j+1)*N+i] = st->X[j*N+i]; 
     720   } 
     721 
     722   /* Convert x (far end) to frequency domain */ 
     723   spx_fft(st->fft_table, st->x, &st->X[0]); 
     724   for (i=0;i<N;i++) 
     725      st->last_y[i] = st->x[i]; 
     726   Sxx = mdf_inner_prod(st->x+st->frame_size, st->x+st->frame_size, st->frame_size); 
     727   for (i=0;i<st->frame_size;i++) 
     728      st->x[i] = st->x[i+st->frame_size]; 
     729   /* From here on, the top part of x is used as scratch space */ 
     730    
    799731#ifdef TWO_PATH 
    800       /* Compute foreground filter */ 
    801       spectral_mul_accum16(st->X, st->foreground+chan*N*K*M, st->Y+chan*N, N, M*K); 
    802       spx_ifft(st->fft_table, st->Y+chan*N, st->e+chan*N); 
    803       for (i=0;i<st->frame_size;i++) 
    804          st->e[chan*N+i] = SUB16(st->input[chan*st->frame_size+i], st->e[chan*N+i+st->frame_size]); 
    805       Sff += mdf_inner_prod(st->e+chan*N, st->e+chan*N, st->frame_size); 
    806 #endif 
    807    } 
     732   /* Compute foreground filter */ 
     733   spectral_mul_accum16(st->X, st->foreground, st->Y, N, M);    
     734   spx_ifft(st->fft_table, st->Y, st->e); 
     735   for (i=0;i<st->frame_size;i++) 
     736      st->e[i] = SUB16(st->input[i], st->e[i+st->frame_size]); 
     737   Sff = mdf_inner_prod(st->e, st->e, st->frame_size); 
     738#endif 
    808739    
    809740   /* Adjust proportional adaption rate */ 
    810    /* FIXME: Adjust that for C, K*/ 
    811    if (st->adapted) 
    812       mdf_adjust_prop (st->W, N, M, C*K, st->prop); 
     741   mdf_adjust_prop (st->W, N, M, st->prop); 
    813742   /* Compute weight gradient */ 
    814743   if (st->saturated == 0) 
    815744   { 
    816       for (chan = 0; chan < C; chan++) 
    817       { 
    818          for (speak = 0; speak < K; speak++) 
    819          { 
    820             for (j=M-1;j>=0;j--) 
    821             { 
    822                weighted_spectral_mul_conj(st->power_1, FLOAT_SHL(PSEUDOFLOAT(st->prop[j]),-15), &st->X[(j+1)*N*K+speak*N], st->E+chan*N, st->PHI, N); 
    823                for (i=0;i<N;i++) 
    824                   st->W[chan*N*K*M + j*N*K + speak*N + i] += st->PHI[i]; 
    825             } 
    826          } 
     745      for (j=M-1;j>=0;j--) 
     746      { 
     747         weighted_spectral_mul_conj(st->power_1, FLOAT_SHL(PSEUDOFLOAT(st->prop[j]),-15), &st->X[(j+1)*N], st->E, st->PHI, N); 
     748         for (i=0;i<N;i++) 
     749            st->W[j*N+i] = ADD32(st->W[j*N+i], st->PHI[i]); 
     750          
    827751      } 
    828752   } else { 
     
    830754   } 
    831755    
    832    /* FIXME: MC conversion required */  
    833756   /* Update weight to prevent circular convolution (MDF / AUMDF) */ 
    834    for (chan = 0; chan < C; chan++) 
    835    { 
    836       for (speak = 0; speak < K; speak++) 
    837       { 
    838          for (j=0;j<M;j++) 
     757   for (j=0;j<M;j++) 
     758   { 
     759      /* This is a variant of the Alternatively Updated MDF (AUMDF) */ 
     760      /* Remove the "if" to make this an MDF filter */ 
     761      if (j==0 || st->cancel_count%(M-1) == j-1) 
     762      { 
     763#ifdef FIXED_POINT 
     764         for (i=0;i<N;i++) 
     765            st->wtmp2[i] = EXTRACT16(PSHR32(st->W[j*N+i],NORMALIZE_SCALEDOWN+16)); 
     766         spx_ifft(st->fft_table, st->wtmp2, st->wtmp); 
     767         for (i=0;i<st->frame_size;i++) 
    839768         { 
    840             /* This is a variant of the Alternatively Updated MDF (AUMDF) */ 
    841             /* Remove the "if" to make this an MDF filter */ 
    842             if (j==0 || st->cancel_count%(M-1) == j-1) 
    843             { 
    844 #ifdef FIXED_POINT 
    845                for (i=0;i<N;i++) 
    846                   st->wtmp2[i] = EXTRACT16(PSHR32(st->W[chan*N*K*M + j*N*K + speak*N + i],NORMALIZE_SCALEDOWN+16)); 
    847                spx_ifft(st->fft_table, st->wtmp2, st->wtmp); 
    848                for (i=0;i<st->frame_size;i++) 
    849                { 
    850                   st->wtmp[i]=0; 
    851                } 
    852                for (i=st->frame_size;i<N;i++) 
    853                { 
    854                   st->wtmp[i]=SHL16(st->wtmp[i],NORMALIZE_SCALEUP); 
    855                } 
    856                spx_fft(st->fft_table, st->wtmp, st->wtmp2); 
    857                /* The "-1" in the shift is a sort of kludge that trades less efficient update speed for decrease noise */ 
    858                for (i=0;i<N;i++) 
    859                   st->W[chan*N*K*M + j*N*K + speak*N + i] -= SHL32(EXTEND32(st->wtmp2[i]),16+NORMALIZE_SCALEDOWN-NORMALIZE_SCALEUP-1); 
    860 #else 
    861                spx_ifft(st->fft_table, &st->W[chan*N*K*M + j*N*K + speak*N], st->wtmp); 
    862                for (i=st->frame_size;i<N;i++) 
    863                { 
    864                   st->wtmp[i]=0; 
    865                } 
    866                spx_fft(st->fft_table, st->wtmp, &st->W[chan*N*K*M + j*N*K + speak*N]); 
    867 #endif 
    868             } 
     769            st->wtmp[i]=0; 
    869770         } 
    870       } 
    871    } 
    872     
    873    /* So we can use power_spectrum_accum */  
    874    for (i=0;i<=st->frame_size;i++) 
    875       st->Rf[i] = st->Yf[i] = st->Xf[i] = 0; 
    876        
    877    Dbf = 0; 
    878    See = 0;     
     771         for (i=st->frame_size;i<N;i++) 
     772         { 
     773            st->wtmp[i]=SHL16(st->wtmp[i],NORMALIZE_SCALEUP); 
     774         } 
     775         spx_fft(st->fft_table, st->wtmp, st->wtmp2); 
     776         /* The "-1" in the shift is a sort of kludge that trades less efficient update speed for decrease noise */ 
     777         for (i=0;i<N;i++) 
     778            st->W[j*N+i] -= SHL32(EXTEND32(st->wtmp2[i]),16+NORMALIZE_SCALEDOWN-NORMALIZE_SCALEUP-1); 
     779#else 
     780         spx_ifft(st->fft_table, &st->W[j*N], st->wtmp); 
     781         for (i=st->frame_size;i<N;i++) 
     782         { 
     783            st->wtmp[i]=0; 
     784         } 
     785         spx_fft(st->fft_table, st->wtmp, &st->W[j*N]); 
     786#endif 
     787      } 
     788   } 
     789 
     790   /* Compute filter response Y */ 
     791   spectral_mul_accum(st->X, st->W, st->Y, N, M); 
     792   spx_ifft(st->fft_table, st->Y, st->y); 
     793 
    879794#ifdef TWO_PATH 
    880795   /* Difference in response, this is used to estimate the variance of our residual power estimate */ 
    881    for (chan = 0; chan < C; chan++) 
    882    { 
    883       spectral_mul_accum(st->X, st->W+chan*N*K*M, st->Y+chan*N, N, M*K); 
    884       spx_ifft(st->fft_table, st->Y+chan*N, st->y+chan*N); 
    885       for (i=0;i<st->frame_size;i++) 
    886          st->e[chan*N+i] = SUB16(st->e[chan*N+i+st->frame_size], st->y[chan*N+i+st->frame_size]); 
    887       Dbf += 10+mdf_inner_prod(st->e+chan*N, st->e+chan*N, st->frame_size); 
    888       for (i=0;i<st->frame_size;i++) 
    889          st->e[chan*N+i] = SUB16(st->input[chan*st->frame_size+i], st->y[chan*N+i+st->frame_size]); 
    890       See += mdf_inner_prod(st->e+chan*N, st->e+chan*N, st->frame_size); 
    891    } 
    892 #endif 
    893  
     796   for (i=0;i<st->frame_size;i++) 
     797      st->e[i] = SUB16(st->e[i+st->frame_size], st->y[i+st->frame_size]); 
     798   Dbf = 10+mdf_inner_prod(st->e, st->e, st->frame_size); 
     799#endif 
     800 
     801   for (i=0;i<st->frame_size;i++) 
     802      st->e[i] = SUB16(st->input[i], st->y[i+st->frame_size]); 
     803   See = mdf_inner_prod(st->e, st->e, st->frame_size); 
    894804#ifndef TWO_PATH 
    895805   Sff = See; 
     
    928838      st->Dvar1 = st->Dvar2 = FLOAT_ZERO; 
    929839      /* Copy background filter to foreground filter */ 
    930       for (i=0;i<N*M*C*K;i++) 
     840      for (i=0;i<N*M;i++) 
    931841         st->foreground[i] = EXTRACT16(PSHR32(st->W[i],16)); 
    932842      /* Apply a smooth transition so as to not introduce blocking artifacts */ 
    933       for (chan = 0; chan < C; chan++) 
    934          for (i=0;i<st->frame_size;i++) 
    935             st->e[chan*N+i+st->frame_size] = MULT16_16_Q15(st->window[i+st->frame_size],st->e[chan*N+i+st->frame_size]) + MULT16_16_Q15(st->window[i],st->y[chan*N+i+st->frame_size]); 
     843      for (i=0;i<st->frame_size;i++) 
     844         st->e[i+st->frame_size] = MULT16_16_Q15(st->window[i+st->frame_size],st->e[i+st->frame_size]) + MULT16_16_Q15(st->window[i],st->y[i+st->frame_size]); 
    936845   } else { 
    937846      int reset_background=0; 
     
    946855      { 
    947856         /* Copy foreground filter to background filter */ 
    948          for (i=0;i<N*M*C*K;i++) 
     857         for (i=0;i<N*M;i++) 
    949858            st->W[i] = SHL32(EXTEND32(st->foreground[i]),16); 
    950859         /* We also need to copy the output so as to get correct adaptation */ 
    951          for (chan = 0; chan < C; chan++) 
    952          {         
    953             for (i=0;i<st->frame_size;i++) 
    954                st->y[chan*N+i+st->frame_size] = st->e[chan*N+i+st->frame_size]; 
    955             for (i=0;i<st->frame_size;i++) 
    956                st->e[chan*N+i] = SUB16(st->input[chan*st->frame_size+i], st->y[chan*N+i+st->frame_size]); 
    957          }         
     860         for (i=0;i<st->frame_size;i++) 
     861            st->y[i+st->frame_size] = st->e[i+st->frame_size]; 
     862         for (i=0;i<st->frame_size;i++) 
     863            st->e[i] = SUB16(st->input[i], st->y[i+st->frame_size]); 
    958864         See = Sff; 
    959865         st->Davg1 = st->Davg2 = 0; 
     
    963869#endif 
    964870 
    965    Sey = Syy = Sdd = 0;   
    966    for (chan = 0; chan < C; chan++) 
    967    {     
    968       /* Compute error signal (for the output with de-emphasis) */  
    969       for (i=0;i<st->frame_size;i++) 
    970       { 
    971          spx_word32_t tmp_out; 
     871   /* Compute error signal (for the output with de-emphasis) */  
     872   for (i=0;i<st->frame_size;i++) 
     873   { 
     874      spx_word32_t tmp_out; 
    972875#ifdef TWO_PATH 
    973          tmp_out = SUB32(EXTEND32(st->input[chan*st->frame_size+i]), EXTEND32(st->e[chan*N+i+st->frame_size])); 
    974 #else 
    975          tmp_out = SUB32(EXTEND32(st->input[chan*st->frame_size+i]), EXTEND32(st->y[chan*N+i+st->frame_size])); 
    976 #endif 
    977          tmp_out = ADD32(tmp_out, EXTEND32(MULT16_16_P15(st->preemph, st->memE[chan]))); 
     876      tmp_out = SUB32(EXTEND32(st->input[i]), EXTEND32(st->e[i+st->frame_size])); 
     877#else 
     878      tmp_out = SUB32(EXTEND32(st->input[i]), EXTEND32(st->y[i+st->frame_size])); 
     879#endif 
     880      /* Saturation */ 
     881      if (tmp_out>32767) 
     882         tmp_out = 32767; 
     883      else if (tmp_out<-32768) 
     884         tmp_out = -32768; 
     885      tmp_out = ADD32(tmp_out, EXTEND32(MULT16_16_P15(st->preemph, st->memE))); 
    978886      /* This is an arbitrary test for saturation in the microphone signal */ 
    979          if (in[i*C+chan] <= -32000 || in[i*C+chan] >= 32000) 
    980          { 
     887      if (in[i] <= -32000 || in[i] >= 32000) 
     888      { 
     889         tmp_out = 0; 
    981890         if (st->saturated == 0) 
    982891            st->saturated = 1; 
    983          } 
    984          out[i*C+chan] = WORD2INT(tmp_out); 
    985          st->memE[chan] = tmp_out; 
    986       } 
    987  
     892      } 
     893      out[i] = (spx_int16_t)tmp_out; 
     894      st->memE = tmp_out; 
     895   } 
     896    
    988897#ifdef DUMP_ECHO_CANCEL_DATA 
    989       dump_audio(in, far_end, out, st->frame_size); 
    990 #endif 
    991     
    992       /* Compute error signal (filter update version) */  
    993       for (i=0;i<st->frame_size;i++) 
    994       { 
    995          st->e[chan*N+i+st->frame_size] = st->e[chan*N+i]; 
    996          st->e[chan*N+i] = 0; 
    997       } 
    998        
    999       /* Compute a bunch of correlations */ 
    1000       /* FIXME: bad merge */ 
    1001       Sey += mdf_inner_prod(st->e+chan*N+st->frame_size, st->y+chan*N+st->frame_size, st->frame_size); 
    1002       Syy += mdf_inner_prod(st->y+chan*N+st->frame_size, st->y+chan*N+st->frame_size, st->frame_size); 
    1003       Sdd += mdf_inner_prod(st->input+chan*st->frame_size, st->input+chan*st->frame_size, st->frame_size); 
    1004        
    1005       /* Convert error to frequency domain */ 
    1006       spx_fft(st->fft_table, st->e+chan*N, st->E+chan*N); 
    1007       for (i=0;i<st->frame_size;i++) 
    1008          st->y[i+chan*N] = 0; 
    1009       spx_fft(st->fft_table, st->y+chan*N, st->Y+chan*N); 
    1010     
    1011       /* Compute power spectrum of echo (X), error (E) and filter response (Y) */ 
    1012       power_spectrum_accum(st->E+chan*N, st->Rf, N); 
    1013       power_spectrum_accum(st->Y+chan*N, st->Yf, N); 
    1014      
    1015    } 
     898   dump_audio(in, far_end, out, st->frame_size); 
     899#endif 
     900    
     901   /* Compute error signal (filter update version) */  
     902   for (i=0;i<st->frame_size;i++) 
     903   { 
     904      st->e[i+st->frame_size] = st->e[i]; 
     905      st->e[i] = 0; 
     906   } 
     907 
     908   /* Compute a bunch of correlations */ 
     909   Sey = mdf_inner_prod(st->e+st->frame_size, st->y+st->frame_size, st->frame_size); 
     910   Syy = mdf_inner_prod(st->y+st->frame_size, st->y+st->frame_size, st->frame_size); 
     911   Sdd = mdf_inner_prod(st->input, st->input, st->frame_size); 
    1016912    
    1017913   /*printf ("%f %f %f %f\n", Sff, See, Syy, Sdd, st->update_cond);*/ 
     
    1026922      /* Things have gone really bad */ 
    1027923      st->screwed_up += 50; 
    1028       for (i=0;i<st->frame_size*C;i++) 
     924      for (i=0;i<st->frame_size;i++) 
    1029925         out[i] = 0; 
    1030926   } else if (SHR32(Sff, 2) > ADD32(Sdd, SHR32(MULT16_16(N, 10000),6))) 
     
    1045941   /* Add a small noise floor to make sure not to have problems when dividing */ 
    1046942   See = MAX32(See, SHR32(MULT16_16(N, 100),6)); 
    1047       
    1048    for (speak = 0; speak < K; speak++) 
    1049    { 
    1050       Sxx += mdf_inner_prod(st->x+speak*N+st->frame_size, st->x+speak*N+st->frame_size, st->frame_size); 
    1051       power_spectrum_accum(st->X+speak*N, st->Xf, N); 
    1052    } 
    1053  
     943 
     944   /* Convert error to frequency domain */ 
     945   spx_fft(st->fft_table, st->e, st->E); 
     946   for (i=0;i<st->frame_size;i++) 
     947      st->y[i] = 0; 
     948   spx_fft(st->fft_table, st->y, st->Y); 
     949 
     950   /* Compute power spectrum of far end (X), error (E) and filter response (Y) */ 
     951   power_spectrum(st->E, st->Rf, N); 
     952   power_spectrum(st->Y, st->Yf, N); 
     953   power_spectrum(st->X, st->Xf, N); 
    1054954    
    1055955   /* Smooth far end energy estimate over time */ 
    1056956   for (j=0;j<=st->frame_size;j++) 
    1057957      st->power[j] = MULT16_32_Q15(ss_1,st->power[j]) + 1 + MULT16_32_Q15(ss,st->Xf[j]); 
     958    
     959   /* Enable this to compute the power based only on the tail (would need to compute more  
     960      efficiently to make this really useful */ 
     961   if (0) 
     962   { 
     963      float scale2 = .5f/M; 
     964      for (j=0;j<=st->frame_size;j++) 
     965         st->power[j] = 100; 
     966      for (i=0;i<M;i++) 
     967      { 
     968         power_spectrum(&st->X[i*N], st->Xf, N); 
     969         for (j=0;j<=st->frame_size;j++) 
     970            st->power[j] += scale2*st->Xf[j]; 
     971      } 
     972   } 
    1058973 
    1059974   /* Compute filtered spectra and (cross-)correlations */ 
     
    11771092   } 
    11781093 
    1179    /* FIXME: MC conversion required */  
     1094   /* Save residual echo so it can be used by the nonlinear processor */ 
     1095   if (st->adapted) 
     1096   { 
     1097      /* If the filter is adapted, take the filtered echo */ 
    11801098      for (i=0;i<st->frame_size;i++) 
    11811099         st->last_y[i] = st->last_y[st->frame_size+i]; 
    1182    if (st->adapted) 
    1183    { 
    1184       /* If the filter is adapted, take the filtered echo */ 
    11851100      for (i=0;i<st->frame_size;i++) 
    11861101         st->last_y[st->frame_size+i] = in[i]-out[i]; 
     
    12271142} 
    12281143 
    1229 EXPORT int speex_echo_ctl(SpeexEchoState *st, int request, void *ptr) 
     1144int speex_echo_ctl(SpeexEchoState *st, int request, void *ptr) 
    12301145{ 
    12311146   switch(request) 
     
    12551170         (*(int*)ptr) = st->sampling_rate; 
    12561171         break; 
    1257       case SPEEX_ECHO_GET_IMPULSE_RESPONSE_SIZE: 
    1258          /*FIXME: Implement this for multiple channels */ 
    1259          *((spx_int32_t *)ptr) = st->M * st->frame_size; 
    1260          break; 
    1261       case SPEEX_ECHO_GET_IMPULSE_RESPONSE: 
    1262       { 
    1263          int M = st->M, N = st->window_size, n = st->frame_size, i, j; 
    1264          spx_int32_t *filt = (spx_int32_t *) ptr; 
    1265          for(j=0;j<M;j++) 
    1266          { 
    1267             /*FIXME: Implement this for multiple channels */ 
    1268 #ifdef FIXED_POINT 
    1269             for (i=0;i<N;i++) 
    1270                st->wtmp2[i] = EXTRACT16(PSHR32(st->W[j*N+i],16+NORMALIZE_SCALEDOWN)); 
    1271             spx_ifft(st->fft_table, st->wtmp2, st->wtmp); 
    1272 #else 
    1273             spx_ifft(st->fft_table, &st->W[j*N], st->wtmp); 
    1274 #endif 
    1275             for(i=0;i<n;i++) 
    1276                filt[j*n+i] = PSHR32(MULT16_16(32767,st->wtmp[i]), WEIGHT_SHIFT-NORMALIZE_SCALEDOWN); 
    1277          } 
    1278       } 
    1279          break; 
    12801172      default: 
    12811173         speex_warning_int("Unknown speex_echo_ctl request: ", request); 
Note: See TracChangeset for help on using the changeset viewer.