blob: 19428597019b8e80ee893e5d94f54d8ce6d81a85 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $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 /* 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 */
131PJ_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 */
154PJ_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 */
164PJ_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