Add support for ICE/STUN/TURN in res_rtp_asterisk and chan_sip.
[asterisk/asterisk.git] / res / pjproject / pjmedia / src / pjmedia / echo_speex.c
1 /* $Id$ */
2 /* 
3  * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4  * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
19  */
20
21 #include <pjmedia/echo.h>
22 #include <pjmedia/errno.h>
23 #include <pjmedia/frame.h>
24 #include <pj/assert.h>
25 #include <pj/log.h>
26 #include <pj/pool.h>
27
28 #if defined(PJMEDIA_HAS_SPEEX_AEC) && PJMEDIA_HAS_SPEEX_AEC != 0
29
30 #include <speex/speex_echo.h>
31 #include <speex/speex_preprocess.h>
32
33 #include "echo_internal.h"
34
35 typedef struct speex_ec
36 {
37     SpeexEchoState       *state;
38     SpeexPreprocessState *preprocess;
39
40     unsigned              samples_per_frame;
41     unsigned              prefetch;
42     unsigned              options;
43     pj_int16_t           *tmp_frame;
44 } speex_ec;
45
46
47
48 /*
49  * Create the AEC. 
50  */
51 PJ_DEF(pj_status_t) speex_aec_create(pj_pool_t *pool,
52                                      unsigned clock_rate,
53                                      unsigned channel_count,
54                                      unsigned samples_per_frame,
55                                      unsigned tail_ms,
56                                      unsigned options,
57                                      void **p_echo )
58 {
59     speex_ec *echo;
60     int sampling_rate;
61
62     *p_echo = NULL;
63
64     echo = PJ_POOL_ZALLOC_T(pool, speex_ec);
65     PJ_ASSERT_RETURN(echo != NULL, PJ_ENOMEM);
66
67     echo->samples_per_frame = samples_per_frame;
68     echo->options = options;
69
70 #if 0
71     echo->state = speex_echo_state_init_mc(echo->samples_per_frame,
72                                            clock_rate * tail_ms / 1000,
73                                            channel_count, channel_count);
74 #else
75     if (channel_count != 1) {
76         PJ_LOG(2,("echo_speex.c", "Multichannel EC is not supported by this "
77                                   "echo canceller. It may not work."));
78     }
79     echo->state = speex_echo_state_init(echo->samples_per_frame,
80                                         clock_rate * tail_ms / 1000);
81 #endif
82     if (echo->state == NULL) {
83         return PJ_ENOMEM;
84     }
85
86     /* Set sampling rate */
87     sampling_rate = clock_rate;
88     speex_echo_ctl(echo->state, SPEEX_ECHO_SET_SAMPLING_RATE, 
89                    &sampling_rate);
90
91     echo->preprocess = speex_preprocess_state_init(echo->samples_per_frame,
92                                                    clock_rate);
93     if (echo->preprocess == NULL) {
94         speex_echo_state_destroy(echo->state);
95         return PJ_ENOMEM;
96     }
97
98     /* Disable all preprocessing, we only want echo cancellation */
99 #if 0
100     disabled = 0;
101     enabled = 1;
102     speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DENOISE, 
103                          &enabled);
104     speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC, 
105                          &disabled);
106     speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_VAD, 
107                          &disabled);
108     speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_DEREVERB, 
109                          &enabled);
110 #endif
111
112     /* Control echo cancellation in the preprocessor */
113    speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE, 
114                         echo->state);
115
116
117     /* Create temporary frame for echo cancellation */
118     echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame);
119     PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM);
120
121     /* Done */
122     *p_echo = echo;
123     return PJ_SUCCESS;
124
125 }
126
127
128 /*
129  * Destroy AEC
130  */
131 PJ_DEF(pj_status_t) speex_aec_destroy(void *state )
132 {
133     speex_ec *echo = (speex_ec*) state;
134
135     PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL);
136
137     if (echo->state) {
138         speex_echo_state_destroy(echo->state);
139         echo->state = NULL;
140     }
141
142     if (echo->preprocess) {
143         speex_preprocess_state_destroy(echo->preprocess);
144         echo->preprocess = NULL;
145     }
146
147     return PJ_SUCCESS;
148 }
149
150
151 /*
152  * Reset AEC
153  */
154 PJ_DEF(void) speex_aec_reset(void *state )
155 {
156     speex_ec *echo = (speex_ec*) state;
157     speex_echo_state_reset(echo->state);
158 }
159
160
161 /*
162  * Perform echo cancellation.
163  */
164 PJ_DEF(pj_status_t) speex_aec_cancel_echo( void *state,
165                                            pj_int16_t *rec_frm,
166                                            const pj_int16_t *play_frm,
167                                            unsigned options,
168                                            void *reserved )
169 {
170     speex_ec *echo = (speex_ec*) state;
171
172     /* Sanity checks */
173     PJ_ASSERT_RETURN(echo && rec_frm && play_frm && options==0 &&
174                      reserved==NULL, PJ_EINVAL);
175
176     /* Cancel echo, put output in temporary buffer */
177     speex_echo_cancellation(echo->state, (const spx_int16_t*)rec_frm,
178                             (const spx_int16_t*)play_frm,
179                             (spx_int16_t*)echo->tmp_frame);
180
181
182     /* Preprocess output */
183     speex_preprocess_run(echo->preprocess, (spx_int16_t*)echo->tmp_frame);
184
185     /* Copy temporary buffer back to original rec_frm */
186     pjmedia_copy_samples(rec_frm, echo->tmp_frame, echo->samples_per_frame);
187
188     return PJ_SUCCESS;
189
190 }
191
192 #endif