- Timestamp:
- Aug 9, 2008 5:40:22 AM (16 years ago)
- Location:
- pjproject/trunk
- Files:
-
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjmedia/build/pjmedia.dsp
r2101 r2198 126 126 # Begin Source File 127 127 128 SOURCE=..\src\pjmedia\echo_internal.h 129 # End Source File 130 # Begin Source File 131 128 132 SOURCE=..\src\pjmedia\echo_port.c 129 133 # End Source File -
pjproject/trunk/pjmedia/include/pjmedia/echo.h
r2039 r2198 58 58 { 59 59 /** 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 /** 60 73 * If PJMEDIA_ECHO_SIMPLE flag is specified during echo canceller 61 74 * 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, 65 84 66 85 /** … … 69 88 * canceller will not be called by different threads at the same time. 70 89 */ 71 PJMEDIA_ECHO_NO_LOCK = 290 PJMEDIA_ECHO_NO_LOCK = 16 72 91 73 92 } pjmedia_echo_flag; … … 103 122 pjmedia_echo_state **p_echo ); 104 123 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 */ 144 PJ_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 ); 105 152 106 153 /** … … 115 162 116 163 /** 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 */ 170 PJ_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. 118 175 * The Echo Canceller will keep the frame in its internal buffer, to be used 119 176 * when cancelling the echo with #pjmedia_echo_capture(). … … 132 189 133 190 /** 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()) 138 194 * as the FES (Far End Speech) reference. 139 195 * -
pjproject/trunk/pjmedia/src/pjmedia/echo_common.c
r2039 r2198 18 18 */ 19 19 20 #include <pjmedia/config.h>21 20 #include <pjmedia/echo.h> 21 #include <pjmedia/delaybuf.h> 22 #include <pjmedia/errno.h> 22 23 #include <pj/assert.h> 24 #include <pj/list.h> 25 #include <pj/log.h> 23 26 #include <pj/pool.h> 24 27 #include "echo_internal.h" 25 28 29 #define THIS_FILE "echo_common.c" 30 26 31 typedef struct ec_operations ec_operations; 27 32 33 struct frame 34 { 35 PJ_DECL_LIST_MEMBER(struct frame); 36 short buf[1]; 37 }; 38 28 39 struct pjmedia_echo_state 29 40 { 41 pj_pool_t *pool; 42 char *obj_name; 43 unsigned samples_per_frame; 30 44 void *state; 31 45 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; 32 54 }; 33 55 … … 35 57 struct ec_operations 36 58 { 59 const char *name; 60 37 61 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 ); 44 68 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 ); 50 70 pj_status_t (*ec_cancel)(void *state, 51 71 pj_int16_t *rec_frm, … … 58 78 static struct ec_operations echo_supp_op = 59 79 { 80 "Echo suppressor", 60 81 &echo_supp_create, 61 82 &echo_supp_destroy, 62 &echo_supp_playback, 63 &echo_supp_capture, 83 &echo_supp_reset, 64 84 &echo_supp_cancel_echo 65 85 }; … … 71 91 */ 72 92 #if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC!=0 73 static struct ec_operations aec_op = 74 { 93 static struct ec_operations speex_aec_op = 94 { 95 "AEC", 75 96 &speex_aec_create, 76 97 &speex_aec_destroy, 77 &speex_aec_playback, 78 &speex_aec_capture, 98 &speex_aec_reset, 79 99 &speex_aec_cancel_echo 80 100 }; 81 82 #else83 #define aec_op echo_supp_op84 101 #endif 85 102 86 103 104 /* 105 * IPP AEC prototypes 106 */ 107 #if defined(PJMEDIA_HAS_INTEL_IPP_AEC) && PJMEDIA_HAS_INTEL_IPP_AEC!=0 108 static 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 87 117 88 118 /* … … 97 127 pjmedia_echo_state **p_echo ) 98 128 { 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 */ 136 PJ_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; 99 146 pjmedia_echo_state *ec; 100 147 pj_status_t status; 101 148 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; 105 166 #endif 106 167 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 { 110 177 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 } 114 207 } 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); 123 218 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 */ 127 229 *p_echo = ec; 128 230 … … 136 238 PJ_DEF(pj_status_t) pjmedia_echo_destroy(pjmedia_echo_state *echo ) 137 239 { 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 */ 249 PJ_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. 145 266 */ 146 267 PJ_DEF(pj_status_t) pjmedia_echo_playback( pjmedia_echo_state *echo, 147 268 pj_int16_t *play_frm ) 148 269 { 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; 150 297 } 151 298 … … 159 306 unsigned options ) 160 307 { 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; 162 336 } 163 337 -
pjproject/trunk/pjmedia/src/pjmedia/echo_internal.h
r2039 r2198 29 29 PJ_DECL(pj_status_t) echo_supp_create(pj_pool_t *pool, 30 30 unsigned clock_rate, 31 unsigned channel_count, 31 32 unsigned samples_per_frame, 32 33 unsigned tail_ms, 33 unsigned latency_ms,34 34 unsigned options, 35 35 void **p_state ); 36 36 PJ_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 ); 37 PJ_DECL(void) echo_supp_reset(void *state); 42 38 PJ_DECL(pj_status_t) echo_supp_cancel_echo(void *state, 43 39 pj_int16_t *rec_frm, … … 48 44 PJ_DECL(pj_status_t) speex_aec_create(pj_pool_t *pool, 49 45 unsigned clock_rate, 46 unsigned channel_count, 50 47 unsigned samples_per_frame, 51 48 unsigned tail_ms, 52 unsigned latency_ms,53 49 unsigned options, 54 50 void **p_state ); 55 51 PJ_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 ); 52 PJ_DECL(void) speex_aec_reset(void *state ); 61 53 PJ_DECL(pj_status_t) speex_aec_cancel_echo(void *state, 62 54 pj_int16_t *rec_frm, … … 65 57 void *reserved ); 66 58 59 PJ_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 ); 66 PJ_DECL(pj_status_t) ipp_aec_destroy(void *state ); 67 PJ_DECL(void) ipp_aec_reset(void *state ); 68 PJ_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 67 74 68 75 PJ_END_DECL -
pjproject/trunk/pjmedia/src/pjmedia/echo_port.c
r2039 r2198 68 68 dn_port->info.samples_per_frame); 69 69 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); 73 74 if (status != PJ_SUCCESS) 74 75 return status; -
pjproject/trunk/pjmedia/src/pjmedia/echo_speex.c
r2039 r2198 20 20 #include <pjmedia/echo.h> 21 21 #include <pjmedia/errno.h> 22 #include <pjmedia/silencedet.h>23 22 #include <pj/assert.h> 24 #include <pj/lock.h>25 #include <pj/log.h>26 #include <pj/os.h>27 23 #include <pj/pool.h> 28 24 #include <speex/speex_echo.h> … … 30 26 31 27 #include "echo_internal.h" 32 33 #define THIS_FILE "echo_speex.c"34 #define BUF_COUNT PJMEDIA_SOUND_BUFFER_COUNT35 #define MIN_PREFETCH 236 #define MAX_PREFETCH (BUF_COUNT*2/3)37 38 39 40 #if 041 # define TRACE_(expr) PJ_LOG(5,expr)42 #else43 # define TRACE_(expr)44 #endif45 46 47 typedef struct pjmedia_frame_queue pjmedia_frame_queue;48 49 struct fq_frame50 {51 PJ_DECL_LIST_MEMBER(struct fq_frame);52 void *buf;53 unsigned size;54 pj_uint32_t seq;55 };56 57 struct pjmedia_frame_queue58 {59 char obj_name[PJ_MAX_OBJ_NAME];60 unsigned frame_size;61 int samples_per_frame;62 unsigned count;63 unsigned max_count;64 struct fq_frame frame_list;65 struct fq_frame free_list;66 67 int seq_delay;68 int prefetch_count;69 };70 71 PJ_DEF(pj_status_t) pjmedia_frame_queue_create( pj_pool_t *pool,72 const char *name,73 unsigned frame_size,74 unsigned samples_per_frame,75 unsigned max_count,76 pjmedia_frame_queue **p_fq)77 {78 pjmedia_frame_queue *fq;79 unsigned i;80 81 fq = PJ_POOL_ZALLOC_T(pool, pjmedia_frame_queue);82 83 pj_ansi_snprintf(fq->obj_name, sizeof(fq->obj_name), name, fq);84 fq->obj_name[sizeof(fq->obj_name)-1] = '\0';85 86 fq->max_count = max_count;87 fq->frame_size = frame_size;88 fq->samples_per_frame = samples_per_frame;89 fq->count = 0;90 91 pj_list_init(&fq->frame_list);92 pj_list_init(&fq->free_list);93 94 for (i=0; i<max_count; ++i) {95 struct fq_frame *f;96 97 f = PJ_POOL_ZALLOC_T(pool, struct fq_frame);98 f->buf = pj_pool_alloc(pool, frame_size);99 100 pj_list_push_back(&fq->free_list, f);101 102 }103 104 *p_fq = fq;105 return PJ_SUCCESS;106 }107 108 PJ_DEF(pj_status_t) pjmedia_frame_queue_init( pjmedia_frame_queue *fq,109 int seq_delay,110 int prefetch_count)111 {112 if (prefetch_count > MAX_PREFETCH)113 prefetch_count = MAX_PREFETCH;114 115 fq->seq_delay = seq_delay;116 fq->prefetch_count = prefetch_count;117 fq->count = 0;118 pj_list_merge_first(&fq->free_list, &fq->frame_list);119 120 PJ_LOG(5,(fq->obj_name, "AEC reset, delay=%d, prefetch=%d",121 fq->seq_delay, fq->prefetch_count));122 123 return PJ_SUCCESS;124 }125 126 PJ_DEF(pj_bool_t) pjmedia_frame_queue_empty( pjmedia_frame_queue *fq )127 {128 return pj_list_empty(&fq->frame_list);129 }130 131 PJ_DEF(int) pjmedia_frame_queue_get_prefetch( pjmedia_frame_queue *fq )132 {133 return fq->prefetch_count;134 }135 136 PJ_DEF(pj_status_t) pjmedia_frame_queue_put( pjmedia_frame_queue *fq,137 const void *framebuf,138 unsigned size,139 pj_uint32_t timestamp )140 {141 struct fq_frame *f;142 143 TRACE_((fq->obj_name, "PUT seq=%d, count=%d",144 timestamp / fq->samples_per_frame, fq->count));145 146 if (pj_list_empty(&fq->free_list)) {147 PJ_LOG(5,(fq->obj_name,148 " AEC info: queue is full, frame discarded "149 "[count=%d, seq=%d]",150 fq->max_count, timestamp / fq->samples_per_frame));151 //pjmedia_frame_queue_init(fq, fq->seq_delay, fq->prefetch_count);152 return PJ_ETOOMANY;153 }154 155 PJ_ASSERT_RETURN(size <= fq->frame_size, PJ_ETOOBIG);156 157 f = fq->free_list.next;158 pj_list_erase(f);159 160 pj_memcpy(f->buf, framebuf, size);161 f->size = size;162 f->seq = timestamp / fq->samples_per_frame;163 164 pj_list_push_back(&fq->frame_list, f);165 ++fq->count;166 167 return PJ_SUCCESS;168 }169 170 PJ_DEF(pj_status_t) pjmedia_frame_queue_get( pjmedia_frame_queue *fq,171 pj_uint32_t get_timestamp,172 void **framebuf,173 unsigned *size )174 {175 pj_uint32_t frame_seq;176 struct fq_frame *f;177 178 frame_seq = get_timestamp/fq->samples_per_frame + fq->seq_delay -179 fq->prefetch_count;180 181 TRACE_((fq->obj_name, "GET seq=%d for seq=%d delay=%d, prefetch=%d",182 get_timestamp/fq->samples_per_frame, frame_seq, fq->seq_delay,183 fq->prefetch_count));184 185 *size = 0;186 187 /* Remove old frames */188 for (;!pj_list_empty(&fq->frame_list);) {189 f = fq->frame_list.next;190 if (f->seq >= frame_seq)191 break;192 193 PJ_LOG(5,(fq->obj_name,194 " AEC Info: old frame removed (seq=%d, want=%d, count=%d)",195 f->seq, frame_seq, fq->count));196 pj_list_erase(f);197 --fq->count;198 pj_list_push_back(&fq->free_list, f);199 }200 201 if (pj_list_empty(&fq->frame_list)) {202 PJ_LOG(5,(fq->obj_name,203 " AEC Info: empty queue for seq=%d!",204 frame_seq));205 return PJ_ENOTFOUND;206 }207 208 f = fq->frame_list.next;209 210 if (f->seq > frame_seq) {211 PJ_LOG(5,(fq->obj_name,212 " AEC Info: prefetching (first seq=%d)",213 f->seq));214 return -1;215 }216 217 pj_list_erase(f);218 --fq->count;219 220 *framebuf = (void*)f->buf;221 *size = f->size;222 223 TRACE_((fq->obj_name, " returning frame with seq=%d, count=%d",224 f->seq, fq->count));225 226 pj_list_push_front(&fq->free_list, f);227 return PJ_SUCCESS;228 }229 230 enum231 {232 TS_FLAG_PLAY = 1,233 TS_FLAG_REC = 2,234 TS_FLAG_OK = 3,235 };236 28 237 29 typedef struct speex_ec … … 244 36 unsigned options; 245 37 pj_int16_t *tmp_frame; 246 spx_int32_t *residue;247 248 pj_uint32_t play_ts,249 rec_ts,250 ts_flag;251 252 pjmedia_frame_queue *frame_queue;253 pj_lock_t *lock; /* To protect buffers, if required */254 38 } speex_ec; 255 39 … … 261 45 PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool, 262 46 unsigned clock_rate, 47 unsigned channel_count, 263 48 unsigned samples_per_frame, 264 49 unsigned tail_ms, 265 unsigned latency_ms,266 50 unsigned options, 267 51 void **p_echo ) … … 269 53 speex_ec *echo; 270 54 int sampling_rate; 271 pj_status_t status;272 55 273 56 *p_echo = NULL; … … 276 59 PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM); 277 60 278 if (options & PJMEDIA_ECHO_NO_LOCK) {279 status = pj_lock_create_null_mutex(pool, "aec%p", &echo->lock);280 if (status != PJ_SUCCESS)281 return status;282 } else {283 status = pj_lock_create_simple_mutex(pool, "aec%p", &echo->lock);284 if (status != PJ_SUCCESS)285 return status;286 }287 288 61 echo->samples_per_frame = samples_per_frame; 289 echo->prefetch = (latency_ms * clock_rate / 1000) / samples_per_frame;290 if (echo->prefetch < MIN_PREFETCH)291 echo->prefetch = MIN_PREFETCH;292 if (echo->prefetch > MAX_PREFETCH)293 echo->prefetch = MAX_PREFETCH;294 62 echo->options = options; 295 63 296 echo->state = speex_echo_state_init(samples_per_frame, 297 clock_rate * tail_ms / 1000); 64 #if 0 65 echo->state = speex_echo_state_init_mc(echo->samples_per_frame, 66 clock_rate * tail_ms / 1000, 67 channel_count, channel_count); 68 #else 69 PJ_ASSERT_RETURN(channel_count==1, PJ_EINVAL); 70 echo->state = speex_echo_state_init(echo->samples_per_frame, 71 clock_rate * tail_ms / 1000); 72 #endif 298 73 if (echo->state == NULL) { 299 pj_lock_destroy(echo->lock);300 74 return PJ_ENOMEM; 301 75 } … … 306 80 &sampling_rate); 307 81 308 echo->preprocess = speex_preprocess_state_init( samples_per_frame,82 echo->preprocess = speex_preprocess_state_init(echo->samples_per_frame, 309 83 clock_rate); 310 84 if (echo->preprocess == NULL) { 311 85 speex_echo_state_destroy(echo->state); 312 pj_lock_destroy(echo->lock);313 86 return PJ_ENOMEM; 314 87 } … … 325 98 &disabled); 326 99 speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB, 327 & disabled);100 &enabled); 328 101 #endif 329 102 … … 334 107 335 108 /* Create temporary frame for echo cancellation */ 336 echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2 *samples_per_frame);109 echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame); 337 110 PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM); 338 339 /* Create temporary frame to receive residue */340 echo->residue = (spx_int32_t*)341 pj_pool_zalloc(pool, sizeof(spx_int32_t) *342 (samples_per_frame+1));343 PJ_ASSERT_RETURN(echo->residue != NULL, PJ_ENOMEM);344 345 /* Create frame queue */346 status = pjmedia_frame_queue_create(pool, "aec%p", samples_per_frame*2,347 samples_per_frame, BUF_COUNT,348 &echo->frame_queue);349 if (status != PJ_SUCCESS) {350 speex_preprocess_state_destroy(echo->preprocess);351 speex_echo_state_destroy(echo->state);352 pj_lock_destroy(echo->lock);353 return status;354 }355 111 356 112 /* Done */ 357 113 *p_echo = echo; 358 359 PJ_LOG(4,(THIS_FILE, "Speex Echo canceller/AEC created, clock_rate=%d, "360 "samples per frame=%d, tail length=%d ms, "361 "latency=%d ms",362 clock_rate, samples_per_frame, tail_ms, latency_ms));363 114 return PJ_SUCCESS; 364 115 … … 375 126 PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL); 376 127 377 if (echo->lock)378 pj_lock_acquire(echo->lock);379 380 128 if (echo->state) { 381 129 speex_echo_state_destroy(echo->state); … … 388 136 } 389 137 390 if (echo->lock) {391 pj_lock_destroy(echo->lock);392 echo->lock = NULL;393 }394 395 138 return PJ_SUCCESS; 396 139 } … … 398 141 399 142 /* 400 * Let the AEC knows that a frame has been played to the speaker.143 * Reset AEC 401 144 */ 402 PJ_DEF(pj_status_t) speex_aec_playback(void *state, 403 pj_int16_t *play_frm ) 145 PJ_DEF(void) speex_aec_reset(void *state ) 404 146 { 405 147 speex_ec *echo = (speex_ec*) state; 406 407 /* Sanity checks */ 408 PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL); 409 410 /* The AEC must be configured to support internal playback buffer */ 411 PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP); 412 413 pj_lock_acquire(echo->lock); 414 415 /* Inc timestamp */ 416 echo->play_ts += echo->samples_per_frame; 417 418 /* Initialize frame delay. */ 419 if ((echo->ts_flag & TS_FLAG_PLAY) == 0) { 420 echo->ts_flag |= TS_FLAG_PLAY; 421 422 if (echo->ts_flag == TS_FLAG_OK) { 423 int seq_delay; 424 425 seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) / 426 (int)echo->samples_per_frame; 427 pjmedia_frame_queue_init(echo->frame_queue, seq_delay, 428 echo->prefetch); 429 } 430 } 431 432 if (pjmedia_frame_queue_put(echo->frame_queue, play_frm, 433 echo->samples_per_frame*2, 434 echo->play_ts) != PJ_SUCCESS) 435 { 436 int seq_delay; 437 438 /* On full reset frame queue */ 439 seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) / 440 (int)echo->samples_per_frame; 441 pjmedia_frame_queue_init(echo->frame_queue, seq_delay, 442 echo->prefetch); 443 444 /* And re-put */ 445 pjmedia_frame_queue_put(echo->frame_queue, play_frm, 446 echo->samples_per_frame*2, 447 echo->play_ts); 448 } 449 450 pj_lock_release(echo->lock); 451 452 return PJ_SUCCESS; 453 } 454 455 456 /* 457 * Let the AEC knows that a frame has been captured from the microphone. 458 */ 459 PJ_DEF(pj_status_t) speex_aec_capture( void *state, 460 pj_int16_t *rec_frm, 461 unsigned options ) 462 { 463 speex_ec *echo = (speex_ec*) state; 464 pj_status_t status = PJ_SUCCESS; 465 466 /* Sanity checks */ 467 PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL); 468 469 /* The AEC must be configured to support internal playback buffer */ 470 PJ_ASSERT_RETURN(echo->frame_queue!= NULL, PJ_EINVALIDOP); 471 472 /* Lock mutex */ 473 pj_lock_acquire(echo->lock); 474 475 /* Inc timestamp */ 476 echo->rec_ts += echo->samples_per_frame; 477 478 /* Init frame delay. */ 479 if ((echo->ts_flag & TS_FLAG_REC) == 0) { 480 echo->ts_flag |= TS_FLAG_REC; 481 482 if (echo->ts_flag == TS_FLAG_OK) { 483 int seq_delay; 484 485 seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) / 486 (int)echo->samples_per_frame; 487 pjmedia_frame_queue_init(echo->frame_queue, seq_delay, 488 echo->prefetch); 489 } 490 } 491 492 /* Cancel echo */ 493 if (echo->ts_flag == TS_FLAG_OK) { 494 void *play_buf; 495 unsigned size = 0; 496 497 if (pjmedia_frame_queue_empty(echo->frame_queue)) { 498 int seq_delay; 499 500 seq_delay = ((int)echo->play_ts - (int)echo->rec_ts) / 501 (int)echo->samples_per_frame; 502 pjmedia_frame_queue_init(echo->frame_queue, seq_delay, 503 echo->prefetch); 504 status = -1; 505 506 } else { 507 status = pjmedia_frame_queue_get(echo->frame_queue, echo->rec_ts, 508 &play_buf, &size); 509 if (size != 0) { 510 speex_aec_cancel_echo(echo, rec_frm, (pj_int16_t*)play_buf, 511 options, NULL); 512 } 513 } 514 515 if (status != PJ_SUCCESS) 516 speex_echo_state_reset(echo->state); 517 } 518 519 pj_lock_release(echo->lock); 520 return PJ_SUCCESS; 148 speex_echo_state_reset(echo->state); 521 149 } 522 150 -
pjproject/trunk/pjmedia/src/pjmedia/echo_suppress.c
r2039 r2198 36 36 typedef struct echo_supp 37 37 { 38 pj_bool_t suppressing;39 38 pjmedia_silence_det *sd; 40 pj_time_val last_signal;41 39 unsigned samples_per_frame; 42 40 unsigned tail_ms; … … 50 48 PJ_DEF(pj_status_t) echo_supp_create( pj_pool_t *pool, 51 49 unsigned clock_rate, 50 unsigned channel_count, 52 51 unsigned samples_per_frame, 53 52 unsigned tail_ms, 54 unsigned latency_ms,55 53 unsigned options, 56 54 void **p_state ) … … 60 58 61 59 PJ_UNUSED_ARG(clock_rate); 60 PJ_UNUSED_ARG(channel_count); 62 61 PJ_UNUSED_ARG(options); 63 PJ_UNUSED_ARG(latency_ms);64 62 65 63 ec = PJ_POOL_ZALLOC_T(pool, struct echo_supp); … … 92 90 93 91 /* 94 * Let the AEC knows that a frame has been played to the speaker.92 * Reset 95 93 */ 96 PJ_DEF(pj_status_t) echo_supp_playback( void *state, 97 pj_int16_t *play_frm ) 94 PJ_DEF(void) echo_supp_reset(void *state) 98 95 { 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; 119 98 } 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!=0142 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 #else148 pjmedia_zero_samples(rec_frm, ec->samples_per_frame);149 #endif150 }151 152 return PJ_SUCCESS;153 }154 155 99 156 100 /* -
pjproject/trunk/pjmedia/src/pjmedia/sound_port.c
r2039 r2198 545 545 si.rec_latency = si.play_latency = 0; 546 546 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); 553 557 if (status != PJ_SUCCESS) 554 558 snd_port->ec_state = NULL; -
pjproject/trunk/pjsip-apps/src/samples/aectest.c
r2039 r2198 33 33 #include <pjlib.h> 34 34 35 /* For logging purpose. */ 36 #define THIS_FILE "playfile.c" 35 #define THIS_FILE "aectest.c" 37 36 #define PTIME 20 38 #define TAIL_LENGTH 80037 #define TAIL_LENGTH 200 39 38 40 39 static const char *desc = … … 49 48 " USAGE \n" 50 49 " \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 */ 57 66 58 67 static void app_perror(const char *sender, const char *title, pj_status_t st) … … 73 82 pjmedia_endpt *med_endpt; 74 83 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; 80 87 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)"); 85 129 puts(desc); 86 130 return 1; 87 131 } 88 89 132 90 133 /* Must init PJLIB first: */ … … 110 153 ); 111 154 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 } 190 163 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))); 197 236 198 237 /* 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); 204 247 205 248 /* Release application pool */ -
pjproject/trunk/third_party/speex/libspeex/mdf.c
r2184 r2198 1 /* Copyright (C) 2003-200 8Jean-Marc Valin1 /* Copyright (C) 2003-2006 Jean-Marc Valin 2 2 3 3 File: mdf.c … … 89 89 #endif 90 90 91 #ifdef FIXED_POINT92 #define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))93 #else94 #define WORD2INT(x) ((x) < -32767.5f ? -32768 : ((x) > 32766.5f ? 32767 : floor(.5+(x))))95 #endif96 97 91 /* If enabled, the AEC will use a foreground filter and a background filter to be more robust to double-talk 98 92 and difficult signals in general. The cost is an extra FFT and a matrix-vector multiply */ … … 138 132 int saturated; 139 133 int screwed_up; 140 int C; /** Number of input channels (microphones) */141 int K; /** Number of output channels (loudspeakers) */142 134 spx_int32_t sampling_rate; 143 135 spx_word16_t spec_average; … … 180 172 spx_word16_t *prop; 181 173 void *fft_table; 182 spx_word16_t *memX, *memD, *memE;174 spx_word16_t memX, memD, memE; 183 175 spx_word16_t preemph; 184 176 spx_word16_t notch_radius; 185 spx_mem_t *notch_mem;177 spx_mem_t notch_mem[2]; 186 178 187 179 /* NOTE: If you only use speex_echo_cancel() and want to save some memory, remove this */ … … 191 183 }; 192 184 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)185 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) 194 186 { 195 187 int i; … … 203 195 for (i=0;i<len;i++) 204 196 { 205 spx_word16_t vin = in[i *stride];197 spx_word16_t vin = in[i]; 206 198 spx_word32_t vout = mem[0] + SHL32(EXTEND32(vin),15); 207 199 #ifdef FIXED_POINT … … 243 235 } 244 236 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 257 237 /** Compute cross-power spectrum of a half-complex (packed) vectors and add to acc */ 258 238 #ifdef FIXED_POINT … … 351 331 } 352 332 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;333 static inline void mdf_adjust_prop(const spx_word32_t *W, int N, int M, spx_word16_t *prop) 334 { 335 int i, j; 356 336 spx_word16_t max_sum = 1; 357 337 spx_word32_t prop_sum = 1; … … 359 339 { 360 340 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))); 364 343 #ifdef FIXED_POINT 365 344 /* Just a security in case an overflow were to occur */ … … 400 379 401 380 /** 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; 381 SpeexEchoState *speex_echo_state_init(int frame_size, int filter_length) 382 { 383 int i,N,M; 410 384 SpeexEchoState *st = (SpeexEchoState *)speex_alloc(sizeof(SpeexEchoState)); 411 385 412 st->K = nb_speakers;413 st->C = nb_mic;414 C=st->C;415 K=st->K;416 386 #ifdef DUMP_ECHO_CANCEL_DATA 417 387 if (rFile || pFile || oFile) … … 444 414 st->fft_table = spx_fft_init(N); 445 415 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)); 451 421 st->Yf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); 452 422 st->Rf = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); … … 455 425 st->Eh = (spx_word32_t*)speex_alloc((st->frame_size+1)*sizeof(spx_word32_t)); 456 426 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)); 461 431 #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)); 463 433 #endif 464 434 st->PHI = (spx_word32_t*)speex_alloc(N*sizeof(spx_word32_t)); … … 481 451 for (i=0;i<=st->frame_size;i++) 482 452 st->power_1[i] = FLOAT_ONE; 483 for (i=0;i<N*M *K*C;i++)453 for (i=0;i<N*M;i++) 484 454 st->W[i] = 0; 485 455 { … … 496 466 for (i=M-1;i>=0;i--) 497 467 { 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; 505 473 st->preemph = QCONST16(.9,15); 506 474 if (st->sampling_rate<12000) … … 511 479 st->notch_radius = QCONST16(.992, 15); 512 480 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; 514 482 st->adapted = 0; 515 483 st->Pey = st->Pyy = FLOAT_ONE; … … 520 488 #endif 521 489 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)); 523 491 st->play_buf_pos = PLAYBACK_DELAY*st->frame_size; 524 492 st->play_buf_started = 0; … … 528 496 529 497 /** Resets echo canceller state */ 530 EXPORTvoid speex_echo_state_reset(SpeexEchoState *st)531 { 532 int i, M, N , C, K;498 void speex_echo_state_reset(SpeexEchoState *st) 499 { 500 int i, M, N; 533 501 st->cancel_count=0; 534 502 st->screwed_up = 0; 535 503 N = st->window_size; 536 504 M = st->M; 537 C=st->C;538 K=st->K;539 505 for (i=0;i<N*M;i++) 540 506 st->W[i] = 0; … … 556 522 st->last_y[i] = 0; 557 523 } 558 for (i=0;i<N *C;i++)524 for (i=0;i<N;i++) 559 525 { 560 526 st->E[i] = 0; 561 }562 for (i=0;i<N*K;i++)563 {564 527 st->x[i] = 0; 565 528 } 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; 572 531 573 532 st->saturated = 0; … … 587 546 588 547 /** Destroys an echo canceller state */ 589 EXPORTvoid speex_echo_state_destroy(SpeexEchoState *st)548 void speex_echo_state_destroy(SpeexEchoState *st) 590 549 { 591 550 spx_fft_destroy(st->fft_table); … … 618 577 speex_free(st->wtmp2); 619 578 #endif 620 speex_free(st->memX);621 speex_free(st->memD);622 speex_free(st->memE);623 speex_free(st->notch_mem);624 625 579 speex_free(st->play_buf); 626 580 speex_free(st); … … 634 588 } 635 589 636 EXPORTvoid speex_echo_capture(SpeexEchoState *st, const spx_int16_t *rec, spx_int16_t *out)590 void speex_echo_capture(SpeexEchoState *st, const spx_int16_t *rec, spx_int16_t *out) 637 591 { 638 592 int i; … … 657 611 } 658 612 659 EXPORTvoid speex_echo_playback(SpeexEchoState *st, const spx_int16_t *play)613 void speex_echo_playback(SpeexEchoState *st, const spx_int16_t *play) 660 614 { 661 615 /*speex_warning_int("playback with fill level ", st->play_buf_pos/st->frame_size);*/ … … 684 638 685 639 /** Performs echo cancellation on a frame (deprecated, last arg now ignored) */ 686 EXPORTvoid speex_echo_cancel(SpeexEchoState *st, const spx_int16_t *in, const spx_int16_t *far_end, spx_int16_t *out, spx_int32_t *Yout)640 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) 687 641 { 688 642 speex_echo_cancellation(st, in, far_end, out); … … 690 644 691 645 /** Performs echo cancellation on a frame */ 692 EXPORTvoid 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;646 void 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; 696 650 spx_word32_t Syy,See,Sxx,Sdd, Sff; 697 651 #ifdef TWO_PATH … … 708 662 N = st->window_size; 709 663 M = st->M; 710 C = st->C;711 K = st->K;712 713 664 st->cancel_count++; 714 665 #ifdef FIXED_POINT … … 720 671 #endif 721 672 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 799 731 #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 808 739 809 740 /* 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); 813 742 /* Compute weight gradient */ 814 743 if (st->saturated == 0) 815 744 { 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 827 751 } 828 752 } else { … … 830 754 } 831 755 832 /* FIXME: MC conversion required */833 756 /* 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++) 839 768 { 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; 869 770 } 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 879 794 #ifdef TWO_PATH 880 795 /* 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); 894 804 #ifndef TWO_PATH 895 805 Sff = See; … … 928 838 st->Dvar1 = st->Dvar2 = FLOAT_ZERO; 929 839 /* Copy background filter to foreground filter */ 930 for (i=0;i<N*M *C*K;i++)840 for (i=0;i<N*M;i++) 931 841 st->foreground[i] = EXTRACT16(PSHR32(st->W[i],16)); 932 842 /* 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]); 936 845 } else { 937 846 int reset_background=0; … … 946 855 { 947 856 /* Copy foreground filter to background filter */ 948 for (i=0;i<N*M *C*K;i++)857 for (i=0;i<N*M;i++) 949 858 st->W[i] = SHL32(EXTEND32(st->foreground[i]),16); 950 859 /* 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]); 958 864 See = Sff; 959 865 st->Davg1 = st->Davg2 = 0; … … 963 869 #endif 964 870 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; 972 875 #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))); 978 886 /* 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; 981 890 if (st->saturated == 0) 982 891 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 988 897 #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); 1016 912 1017 913 /*printf ("%f %f %f %f\n", Sff, See, Syy, Sdd, st->update_cond);*/ … … 1026 922 /* Things have gone really bad */ 1027 923 st->screwed_up += 50; 1028 for (i=0;i<st->frame_size *C;i++)924 for (i=0;i<st->frame_size;i++) 1029 925 out[i] = 0; 1030 926 } else if (SHR32(Sff, 2) > ADD32(Sdd, SHR32(MULT16_16(N, 10000),6))) … … 1045 941 /* Add a small noise floor to make sure not to have problems when dividing */ 1046 942 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); 1054 954 1055 955 /* Smooth far end energy estimate over time */ 1056 956 for (j=0;j<=st->frame_size;j++) 1057 957 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 } 1058 973 1059 974 /* Compute filtered spectra and (cross-)correlations */ … … 1177 1092 } 1178 1093 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 */ 1180 1098 for (i=0;i<st->frame_size;i++) 1181 1099 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 */1185 1100 for (i=0;i<st->frame_size;i++) 1186 1101 st->last_y[st->frame_size+i] = in[i]-out[i]; … … 1227 1142 } 1228 1143 1229 EXPORTint speex_echo_ctl(SpeexEchoState *st, int request, void *ptr)1144 int speex_echo_ctl(SpeexEchoState *st, int request, void *ptr) 1230 1145 { 1231 1146 switch(request) … … 1255 1170 (*(int*)ptr) = st->sampling_rate; 1256 1171 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_POINT1269 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 #else1273 spx_ifft(st->fft_table, &st->W[j*N], st->wtmp);1274 #endif1275 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;1280 1172 default: 1281 1173 speex_warning_int("Unknown speex_echo_ctl request: ", request);
Note: See TracChangeset
for help on using the changeset viewer.