blob: 18f42b3b5568f97ead444845f71e263328dc7b75 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $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
35typedef 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 */
51PJ_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 /* Enable AGC */
113 {
114 spx_int32_t enabled = 1;
115 speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_AGC,
116 &enabled);
117 }
118
119 /* Control echo cancellation in the preprocessor */
120 speex_preprocess_ctl(echo->preprocess, SPEEX_PREPROCESS_SET_ECHO_STATE,
121 echo->state);
122
123
124 /* Create temporary frame for echo cancellation */
125 echo->tmp_frame = (pj_int16_t*) pj_pool_zalloc(pool, 2*samples_per_frame);
126 PJ_ASSERT_RETURN(echo->tmp_frame != NULL, PJ_ENOMEM);
127
128 /* Done */
129 *p_echo = echo;
130 return PJ_SUCCESS;
131
132}
133
134
135/*
136 * Destroy AEC
137 */
138PJ_DEF(pj_status_t) speex_aec_destroy(void *state )
139{
140 speex_ec *echo = (speex_ec*) state;
141
142 PJ_ASSERT_RETURN(echo && echo->state, PJ_EINVAL);
143
144 if (echo->state) {
145 speex_echo_state_destroy(echo->state);
146 echo->state = NULL;
147 }
148
149 if (echo->preprocess) {
150 speex_preprocess_state_destroy(echo->preprocess);
151 echo->preprocess = NULL;
152 }
153
154 return PJ_SUCCESS;
155}
156
157
158/*
159 * Reset AEC
160 */
161PJ_DEF(void) speex_aec_reset(void *state )
162{
163 speex_ec *echo = (speex_ec*) state;
164 speex_echo_state_reset(echo->state);
165}
166
167
168/*
169 * Perform echo cancellation.
170 */
171PJ_DEF(pj_status_t) speex_aec_cancel_echo( void *state,
172 pj_int16_t *rec_frm,
173 const pj_int16_t *play_frm,
174 unsigned options,
175 void *reserved )
176{
177 speex_ec *echo = (speex_ec*) state;
178
179 /* Sanity checks */
180 PJ_ASSERT_RETURN(echo && rec_frm && play_frm && options==0 &&
181 reserved==NULL, PJ_EINVAL);
182
183 /* Cancel echo, put output in temporary buffer */
184 speex_echo_cancellation(echo->state, (const spx_int16_t*)rec_frm,
185 (const spx_int16_t*)play_frm,
186 (spx_int16_t*)echo->tmp_frame);
187
188
189 /* Preprocess output */
190 speex_preprocess_run(echo->preprocess, (spx_int16_t*)echo->tmp_frame);
191
192 /* Copy temporary buffer back to original rec_frm */
193 pjmedia_copy_samples(rec_frm, echo->tmp_frame, echo->samples_per_frame);
194
195 return PJ_SUCCESS;
196
197}
198
199/*
200 * Let AEC know that a frame was queued to be played.
201 */
202PJ_DEF(pj_status_t) speex_aec_playback( void *state,
203 pj_int16_t *play_frm )
204{
205 speex_ec *echo = (speex_ec*) state;
206
207 /* Sanity checks */
208 PJ_ASSERT_RETURN(echo && play_frm, PJ_EINVAL);
209
210 speex_echo_playback(echo->state, (spx_int16_t*)play_frm);
211
212 return PJ_SUCCESS;
213
214}
215
216/*
217 * Perform echo cancellation to captured frame.
218 */
219PJ_DEF(pj_status_t) speex_aec_capture( void *state,
220 pj_int16_t *rec_frm,
221 unsigned options )
222{
223 speex_ec *echo = (speex_ec*) state;
224
225 /* Sanity checks */
226 PJ_ASSERT_RETURN(echo && rec_frm, PJ_EINVAL);
227
228 PJ_UNUSED_ARG(options);
229
230 /* Cancel echo */
231 pjmedia_copy_samples(echo->tmp_frame, rec_frm, echo->samples_per_frame);
232 speex_echo_capture(echo->state,
233 (spx_int16_t*)echo->tmp_frame,
234 (spx_int16_t*)rec_frm);
235
236 /* Apply preprocessing */
237 speex_preprocess_run(echo->preprocess, (spx_int16_t*)rec_frm);
238
239 return PJ_SUCCESS;
240}
241
242
243#endif