2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2008, Digium, Inc.
6 * Brian Degenhardt <bmd@digium.com>
7 * Brett Bryant <bbryant@digium.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Noise reduction and automatic gain control (AGC)
24 * \author Brian Degenhardt <bmd@digium.com>
25 * \author Brett Bryant <bbryant@digium.com>
29 * \extref The Speex library - http://www.speex.org
33 <depend>speex</depend>
34 <depend>speex_preprocess</depend>
40 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
42 #include <speex/speex_preprocess.h>
43 #include "asterisk/module.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/utils.h"
47 #include "asterisk/audiohook.h"
49 #define DEFAULT_AGC_LEVEL 8000.0
51 struct speex_direction_info
{
52 SpeexPreprocessState
*state
; /*!< speex preprocess state object */
53 int agc
; /*!< audio gain control is enabled or not */
54 int denoise
; /*!< denoise is enabled or not */
55 int samples
; /*!< n of 8Khz samples in last frame */
56 float agclevel
; /*!< audio gain control level [1.0 - 32768.0] */
60 struct ast_audiohook audiohook
;
61 struct speex_direction_info
*tx
, *rx
;
64 static void destroy_callback(void *data
)
66 struct speex_info
*si
= data
;
68 ast_audiohook_destroy(&si
->audiohook
);
70 if (si
->rx
&& si
->rx
->state
) {
71 speex_preprocess_state_destroy(si
->rx
->state
);
74 if (si
->tx
&& si
->tx
->state
) {
75 speex_preprocess_state_destroy(si
->tx
->state
);
89 static const struct ast_datastore_info speex_datastore
= {
91 .destroy
= destroy_callback
94 static int speex_callback(struct ast_audiohook
*audiohook
, struct ast_channel
*chan
, struct ast_frame
*frame
, enum ast_audiohook_direction direction
)
96 struct ast_datastore
*datastore
= NULL
;
97 struct speex_direction_info
*sdi
= NULL
;
98 struct speex_info
*si
= NULL
;
100 /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
101 if (audiohook
->status
== AST_AUDIOHOOK_STATUS_DONE
|| frame
->frametype
!= AST_FRAME_VOICE
) {
105 ast_channel_lock(chan
);
106 if (!(datastore
= ast_channel_datastore_find(chan
, &speex_datastore
, NULL
))) {
107 ast_channel_unlock(chan
);
110 ast_channel_unlock(chan
);
112 si
= datastore
->data
;
114 sdi
= (direction
== AST_AUDIOHOOK_DIRECTION_READ
) ? si
->rx
: si
->tx
;
120 if (sdi
->samples
!= frame
->samples
) {
122 speex_preprocess_state_destroy(sdi
->state
);
125 if (!(sdi
->state
= speex_preprocess_state_init((sdi
->samples
= frame
->samples
), 8000))) {
129 speex_preprocess_ctl(sdi
->state
, SPEEX_PREPROCESS_SET_AGC
, &sdi
->agc
);
132 speex_preprocess_ctl(sdi
->state
, SPEEX_PREPROCESS_SET_AGC_LEVEL
, &sdi
->agclevel
);
135 speex_preprocess_ctl(sdi
->state
, SPEEX_PREPROCESS_SET_DENOISE
, &sdi
->denoise
);
138 speex_preprocess(sdi
->state
, frame
->data
.ptr
, NULL
);
143 static int speex_write(struct ast_channel
*chan
, const char *cmd
, char *data
, const char *value
)
145 struct ast_datastore
*datastore
= NULL
;
146 struct speex_info
*si
= NULL
;
147 struct speex_direction_info
**sdi
= NULL
;
150 ast_channel_lock(chan
);
151 if (!(datastore
= ast_channel_datastore_find(chan
, &speex_datastore
, NULL
))) {
152 ast_channel_unlock(chan
);
154 if (!(datastore
= ast_channel_datastore_alloc(&speex_datastore
, NULL
))) {
158 if (!(si
= ast_calloc(1, sizeof(*si
)))) {
159 ast_channel_datastore_free(datastore
);
163 ast_audiohook_init(&si
->audiohook
, AST_AUDIOHOOK_TYPE_MANIPULATE
, "speex");
164 si
->audiohook
.manipulate_callback
= speex_callback
;
168 ast_channel_unlock(chan
);
169 si
= datastore
->data
;
172 if (!strcasecmp(data
, "rx")) {
174 } else if (!strcasecmp(data
, "tx")) {
177 ast_log(LOG_ERROR
, "Invalid argument provided to the %s function\n", cmd
);
180 ast_channel_datastore_free(datastore
);
186 if (!(*sdi
= ast_calloc(1, sizeof(**sdi
)))) {
189 /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
190 * audio. When it supports 16 kHz (or any other sample rates, we will
191 * have to take that into account here. */
192 (*sdi
)->samples
= -1;
195 if (!strcasecmp(cmd
, "agc")) {
196 if (!sscanf(value
, "%f", &(*sdi
)->agclevel
))
197 (*sdi
)->agclevel
= ast_true(value
) ? DEFAULT_AGC_LEVEL
: 0.0;
199 if ((*sdi
)->agclevel
> 32768.0) {
200 ast_log(LOG_WARNING
, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n",
201 ((*sdi
== si
->rx
) ? "rx" : "tx"), (*sdi
)->agclevel
);
202 (*sdi
)->agclevel
= 32768.0;
205 (*sdi
)->agc
= !!((*sdi
)->agclevel
);
208 speex_preprocess_ctl((*sdi
)->state
, SPEEX_PREPROCESS_SET_AGC
, &(*sdi
)->agc
);
210 speex_preprocess_ctl((*sdi
)->state
, SPEEX_PREPROCESS_SET_AGC_LEVEL
, &(*sdi
)->agclevel
);
213 } else if (!strcasecmp(cmd
, "denoise")) {
214 (*sdi
)->denoise
= (ast_true(value
) != 0);
217 speex_preprocess_ctl((*sdi
)->state
, SPEEX_PREPROCESS_SET_DENOISE
, &(*sdi
)->denoise
);
221 if (!(*sdi
)->agc
&& !(*sdi
)->denoise
) {
223 speex_preprocess_state_destroy((*sdi
)->state
);
229 if (!si
->rx
&& !si
->tx
) {
233 ast_channel_lock(chan
);
234 ast_channel_datastore_remove(chan
, datastore
);
235 ast_channel_unlock(chan
);
236 ast_audiohook_remove(chan
, &si
->audiohook
);
237 ast_audiohook_detach(&si
->audiohook
);
240 ast_channel_datastore_free(datastore
);
244 datastore
->data
= si
;
245 ast_channel_lock(chan
);
246 ast_channel_datastore_add(chan
, datastore
);
247 ast_channel_unlock(chan
);
248 ast_audiohook_attach(chan
, &si
->audiohook
);
254 static int speex_read(struct ast_channel
*chan
, const char *cmd
, char *data
, char *buf
, size_t len
)
256 struct ast_datastore
*datastore
= NULL
;
257 struct speex_info
*si
= NULL
;
258 struct speex_direction_info
*sdi
= NULL
;
261 ast_log(LOG_ERROR
, "%s cannot be used without a channel!\n", cmd
);
265 ast_channel_lock(chan
);
266 if (!(datastore
= ast_channel_datastore_find(chan
, &speex_datastore
, NULL
))) {
267 ast_channel_unlock(chan
);
270 ast_channel_unlock(chan
);
272 si
= datastore
->data
;
274 if (!strcasecmp(data
, "tx"))
276 else if (!strcasecmp(data
, "rx"))
279 ast_log(LOG_ERROR
, "%s(%s) must either \"tx\" or \"rx\"\n", cmd
, data
);
283 if (!strcasecmp(cmd
, "agc"))
284 snprintf(buf
, len
, "%.01f", sdi
? sdi
->agclevel
: 0.0);
286 snprintf(buf
, len
, "%d", sdi
? sdi
->denoise
: 0);
291 static struct ast_custom_function agc_function
= {
293 .synopsis
= "Apply automatic gain control to audio on a channel",
295 " The AGC function will apply automatic gain control to audio on the channel\n"
296 "that this function is executed on. Use rx for audio received from the channel\n"
297 "and tx to apply AGC to the audio being sent to the channel. When using this\n"
298 "function, you set a target audio level. It is primarily intended for use with\n"
299 "analog lines, but could be useful for other channels, as well. The target volume\n"
300 "is set with a number between 1 and 32768. Larger numbers are louder.\n"
302 " Set(AGC(rx)=8000)\n"
303 " Set(AGC(tx)=8000)\n"
304 " Set(AGC(rx)=off)\n"
305 " Set(AGC(tx)=off)\n"
307 .write
= speex_write
,
311 static struct ast_custom_function denoise_function
= {
313 .synopsis
= "Apply noise reduction to audio on a channel",
315 " The DENOISE function will apply noise reduction to audio on the channel\n"
316 "that this function is executed on. It is especially useful for noisy analog\n"
317 "lines, especially when adjusting gains or using AGC. Use rx for audio\n"
318 "received from the channel and tx to apply the filter to the audio being sent\n"
321 " Set(DENOISE(rx)=on)\n"
322 " Set(DENOISE(tx)=on)\n"
323 " Set(DENOISE(rx)=off)\n"
324 " Set(DENOISE(tx)=off)\n"
326 .write
= speex_write
,
330 static int unload_module(void)
332 ast_custom_function_unregister(&agc_function
);
333 ast_custom_function_unregister(&denoise_function
);
337 static int load_module(void)
339 if (ast_custom_function_register(&agc_function
)) {
340 return AST_MODULE_LOAD_DECLINE
;
343 if (ast_custom_function_register(&denoise_function
)) {
344 ast_custom_function_unregister(&agc_function
);
345 return AST_MODULE_LOAD_DECLINE
;
348 return AST_MODULE_LOAD_SUCCESS
;
351 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY
, "Noise reduction and Automatic Gain Control (AGC)");