Merged revisions 121078 via svnmerge from
[asterisk-bristuff.git] / funcs / func_speex.c
blob7b24840101f332d94d0075ebe31a9cdd80a90094
1 /*
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.
20 /*! \file
22 * \brief Noise reduction and automatic gain control (AGC)
24 * \author Brian Degenhardt <bmd@digium.com>
25 * \author Brett Bryant <bbryant@digium.com>
27 * \ingroup functions
29 * \extref The Speex library - http://www.speex.org
32 /*** MODULEINFO
33 <depend>speex</depend>
34 <depend>speex_preprocess</depend>
35 <use>speexdsp</use>
36 ***/
38 #include "asterisk.h"
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] */
59 struct speex_info {
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);
78 if (si->rx) {
79 ast_free(si->rx);
82 if (si->tx) {
83 ast_free(si->tx);
86 ast_free(data);
89 static const struct ast_datastore_info speex_datastore = {
90 .type = "speex",
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) {
102 return 0;
105 ast_channel_lock(chan);
106 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
107 ast_channel_unlock(chan);
108 return 0;
110 ast_channel_unlock(chan);
112 si = datastore->data;
114 sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
116 if (!sdi) {
117 return 0;
120 if (sdi->samples != frame->samples) {
121 if (sdi->state) {
122 speex_preprocess_state_destroy(sdi->state);
125 if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), 8000))) {
126 return -1;
129 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
131 if (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);
140 return 0;
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;
148 int is_new = 0;
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))) {
155 return 0;
158 if (!(si = ast_calloc(1, sizeof(*si)))) {
159 ast_channel_datastore_free(datastore);
160 return 0;
163 ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex");
164 si->audiohook.manipulate_callback = speex_callback;
166 is_new = 1;
167 } else {
168 ast_channel_unlock(chan);
169 si = datastore->data;
172 if (!strcasecmp(data, "rx")) {
173 sdi = &si->rx;
174 } else if (!strcasecmp(data, "tx")) {
175 sdi = &si->tx;
176 } else {
177 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
179 if (is_new) {
180 ast_channel_datastore_free(datastore);
181 return -1;
185 if (!*sdi) {
186 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
187 return 0;
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);
207 if ((*sdi)->state) {
208 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
209 if ((*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);
216 if ((*sdi)->state) {
217 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
221 if (!(*sdi)->agc && !(*sdi)->denoise) {
222 if ((*sdi)->state)
223 speex_preprocess_state_destroy((*sdi)->state);
225 ast_free(*sdi);
226 *sdi = NULL;
229 if (!si->rx && !si->tx) {
230 if (is_new) {
231 is_new = 0;
232 } else {
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);
243 if (is_new) {
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);
251 return 0;
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;
260 if (!chan) {
261 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
262 return -1;
265 ast_channel_lock(chan);
266 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
267 ast_channel_unlock(chan);
268 return -1;
270 ast_channel_unlock(chan);
272 si = datastore->data;
274 if (!strcasecmp(data, "tx"))
275 sdi = si->tx;
276 else if (!strcasecmp(data, "rx"))
277 sdi = si->rx;
278 else {
279 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
280 return -1;
283 if (!strcasecmp(cmd, "agc"))
284 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
285 else
286 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
288 return 0;
291 static struct ast_custom_function agc_function = {
292 .name = "AGC",
293 .synopsis = "Apply automatic gain control to audio on a channel",
294 .desc =
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"
301 " Example Usage:\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,
308 .read = speex_read
311 static struct ast_custom_function denoise_function = {
312 .name = "DENOISE",
313 .synopsis = "Apply noise reduction to audio on a channel",
314 .desc =
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"
319 "to the channel.\n"
320 " Example Usage:\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,
327 .read = speex_read
330 static int unload_module(void)
332 ast_custom_function_unregister(&agc_function);
333 ast_custom_function_unregister(&denoise_function);
334 return 0;
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)");