d0d451bd16ee905f0af6944bd1aa42687b79f022
[asterisk/asterisk.git] / funcs / func_speex.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2008, Digium, Inc.
5  *
6  * Brian Degenhardt <bmd@digium.com>
7  * Brett Bryant <bbryant@digium.com> 
8  *
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.
14  *
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.
18  */
19
20 /*! \file
21  *
22  * \brief Noise reduction and automatic gain control (AGC)
23  *
24  * \author Brian Degenhardt <bmd@digium.com> 
25  * \author Brett Bryant <bbryant@digium.com> 
26  *
27  * \ingroup functions
28  *
29  * The Speex 1.2 library - http://www.speex.org
30  * \note Requires the 1.2 version of the Speex library (which might not be what you find in Linux packages)
31  */
32
33 /*** MODULEINFO
34         <depend>speex</depend>
35         <depend>speex_preprocess</depend>
36         <use type="external">speexdsp</use>
37         <support_level>core</support_level>
38  ***/
39
40 #include "asterisk.h"
41
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
43
44 #include <speex/speex_preprocess.h>
45 #include "asterisk/module.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/audiohook.h"
50
51 #define DEFAULT_AGC_LEVEL 8000.0
52
53 /*** DOCUMENTATION
54         <function name="AGC" language="en_US">
55                 <synopsis>
56                         Apply automatic gain control to audio on a channel.
57                 </synopsis>
58                 <syntax>
59                         <parameter name="channeldirection" required="true">
60                                 <para>This can be either <literal>rx</literal> or <literal>tx</literal></para>
61                         </parameter>
62                 </syntax>
63                 <description>
64                         <para>The AGC function will apply automatic gain control to the audio on the
65                         channel that it is executed on. Using <literal>rx</literal> for audio received
66                         and <literal>tx</literal> for audio transmitted to the channel. When using this
67                         function you set a target audio level. It is primarily intended for use with
68                         analog lines, but could be useful for other channels as well. The target volume 
69                         is set with a number between <literal>1-32768</literal>. The larger the number
70                         the louder (more gain) the channel will receive.</para>
71                         <para>Examples:</para>
72                         <para>exten => 1,1,Set(AGC(rx)=8000)</para>
73                         <para>exten => 1,2,Set(AGC(tx)=off)</para>
74                 </description>
75         </function>
76         <function name="DENOISE" language="en_US">
77                 <synopsis>
78                         Apply noise reduction to audio on a channel.
79                 </synopsis>
80                 <syntax>
81                         <parameter name="channeldirection" required="true">
82                                 <para>This can be either <literal>rx</literal> or <literal>tx</literal> 
83                                 the values that can be set to this are either <literal>on</literal> and
84                                 <literal>off</literal></para>
85                         </parameter>
86                 </syntax>
87                 <description>
88                         <para>The DENOISE function will apply noise reduction to audio on the channel
89                         that it is executed on. It is very useful for noisy analog lines, especially
90                         when adjusting gains or using AGC. Use <literal>rx</literal> for audio received from the channel
91                         and <literal>tx</literal> to apply the filter to the audio being sent to the channel.</para>
92                         <para>Examples:</para>
93                         <para>exten => 1,1,Set(DENOISE(rx)=on)</para>
94                         <para>exten => 1,2,Set(DENOISE(tx)=off)</para>
95                 </description>
96         </function>
97  ***/
98
99 struct speex_direction_info {
100         SpeexPreprocessState *state;    /*!< speex preprocess state object */
101         int agc;                                                /*!< audio gain control is enabled or not */
102         int denoise;                                    /*!< denoise is enabled or not */
103         int samples;                                    /*!< n of 8Khz samples in last frame */
104         float agclevel;                                 /*!< audio gain control level [1.0 - 32768.0] */
105 };
106
107 struct speex_info {
108         struct ast_audiohook audiohook;
109         int lastrate;
110         struct speex_direction_info *tx, *rx;
111 };
112
113 static void destroy_callback(void *data) 
114 {
115         struct speex_info *si = data;
116
117         ast_audiohook_destroy(&si->audiohook);
118
119         if (si->rx && si->rx->state) {
120                 speex_preprocess_state_destroy(si->rx->state);
121         }
122
123         if (si->tx && si->tx->state) {
124                 speex_preprocess_state_destroy(si->tx->state);
125         }
126
127         if (si->rx) {
128                 ast_free(si->rx);
129         }
130
131         if (si->tx) {
132                 ast_free(si->tx);
133         }
134
135         ast_free(data);
136 };
137
138 static const struct ast_datastore_info speex_datastore = {
139         .type = "speex",
140         .destroy = destroy_callback
141 };
142
143 static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
144 {
145         struct ast_datastore *datastore = NULL;
146         struct speex_direction_info *sdi = NULL;
147         struct speex_info *si = NULL;
148         char source[80];
149
150         /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
151         if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || frame->frametype != AST_FRAME_VOICE) {
152                 return -1;
153         }
154
155         /* We are called with chan already locked */
156         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
157                 return -1;
158         }
159
160         si = datastore->data;
161
162         sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
163
164         if (!sdi) {
165                 return -1;
166         }
167
168         if ((sdi->samples != frame->samples) || (ast_format_get_sample_rate(frame->subclass.format) != si->lastrate)) {
169                 si->lastrate = ast_format_get_sample_rate(frame->subclass.format);
170                 if (sdi->state) {
171                         speex_preprocess_state_destroy(sdi->state);
172                 }
173
174                 if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), si->lastrate))) {
175                         return -1;
176                 }
177
178                 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
179
180                 if (sdi->agc) {
181                         speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
182                 }
183
184                 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
185         }
186
187         speex_preprocess(sdi->state, frame->data.ptr, NULL);
188         snprintf(source, sizeof(source), "%s/speex", frame->src);
189         if (frame->mallocd & AST_MALLOCD_SRC) {
190                 ast_free((char *) frame->src);
191         }
192         frame->src = ast_strdup(source);
193         frame->mallocd |= AST_MALLOCD_SRC;
194
195         return 0;
196 }
197
198 static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
199 {
200         struct ast_datastore *datastore = NULL;
201         struct speex_info *si = NULL;
202         struct speex_direction_info **sdi = NULL;
203         int is_new = 0;
204
205         if (!chan) {
206                 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
207                 return -1;
208         }
209
210         if (strcasecmp(data, "rx") && strcasecmp(data, "tx")) {
211                 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
212                 return -1;
213         }
214
215         ast_channel_lock(chan);
216         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
217                 ast_channel_unlock(chan);
218
219                 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
220                         return 0;
221                 }
222
223                 if (!(si = ast_calloc(1, sizeof(*si)))) {
224                         ast_datastore_free(datastore);
225                         return 0;
226                 }
227
228                 ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
229                 si->audiohook.manipulate_callback = speex_callback;
230                 si->lastrate = 8000;
231                 is_new = 1;
232         } else {
233                 ast_channel_unlock(chan);
234                 si = datastore->data;
235         }
236
237         if (!strcasecmp(data, "rx")) {
238                 sdi = &si->rx;
239         } else {
240                 sdi = &si->tx;
241         }
242
243         if (!*sdi) {
244                 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
245                         return 0;
246                 }
247                 /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
248                  * audio.  When it supports 16 kHz (or any other sample rates, we will
249                  * have to take that into account here. */
250                 (*sdi)->samples = -1;
251         }
252
253         if (!strcasecmp(cmd, "agc")) {
254                 if (!sscanf(value, "%30f", &(*sdi)->agclevel))
255                         (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
256         
257                 if ((*sdi)->agclevel > 32768.0) {
258                         ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n", 
259                                         ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
260                         (*sdi)->agclevel = 32768.0;
261                 }
262         
263                 (*sdi)->agc = !!((*sdi)->agclevel);
264
265                 if ((*sdi)->state) {
266                         speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
267                         if ((*sdi)->agc) {
268                                 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
269                         }
270                 }
271         } else if (!strcasecmp(cmd, "denoise")) {
272                 (*sdi)->denoise = (ast_true(value) != 0);
273
274                 if ((*sdi)->state) {
275                         speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
276                 }
277         }
278
279         if (!(*sdi)->agc && !(*sdi)->denoise) {
280                 if ((*sdi)->state)
281                         speex_preprocess_state_destroy((*sdi)->state);
282
283                 ast_free(*sdi);
284                 *sdi = NULL;
285         }
286
287         if (!si->rx && !si->tx) {
288                 if (is_new) {
289                         is_new = 0;
290                 } else {
291                         ast_channel_lock(chan);
292                         ast_channel_datastore_remove(chan, datastore);
293                         ast_channel_unlock(chan);
294                         ast_audiohook_remove(chan, &si->audiohook);
295                         ast_audiohook_detach(&si->audiohook);
296                 }
297                 
298                 ast_datastore_free(datastore);
299         }
300
301         if (is_new) { 
302                 datastore->data = si;
303                 ast_channel_lock(chan);
304                 ast_channel_datastore_add(chan, datastore);
305                 ast_channel_unlock(chan);
306                 ast_audiohook_attach(chan, &si->audiohook);
307         }
308
309         return 0;
310 }
311
312 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
313 {
314         struct ast_datastore *datastore = NULL;
315         struct speex_info *si = NULL;
316         struct speex_direction_info *sdi = NULL;
317
318         if (!chan) {
319                 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
320                 return -1;
321         }
322
323         ast_channel_lock(chan);
324         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
325                 ast_channel_unlock(chan);
326                 return -1;
327         }
328         ast_channel_unlock(chan);
329
330         si = datastore->data;
331
332         if (!strcasecmp(data, "tx"))
333                 sdi = si->tx;
334         else if (!strcasecmp(data, "rx"))
335                 sdi = si->rx;
336         else {
337                 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
338                 return -1;
339         }
340
341         if (!strcasecmp(cmd, "agc"))
342                 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
343         else
344                 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
345
346         return 0;
347 }
348
349 static struct ast_custom_function agc_function = {
350         .name = "AGC",
351         .write = speex_write,
352         .read = speex_read,
353         .read_max = 22,
354 };
355
356 static struct ast_custom_function denoise_function = {
357         .name = "DENOISE",
358         .write = speex_write,
359         .read = speex_read,
360         .read_max = 22,
361 };
362
363 static int unload_module(void)
364 {
365         ast_custom_function_unregister(&agc_function);
366         ast_custom_function_unregister(&denoise_function);
367         return 0;
368 }
369
370 static int load_module(void)
371 {
372         if (ast_custom_function_register(&agc_function)) {
373                 return AST_MODULE_LOAD_DECLINE;
374         }
375
376         if (ast_custom_function_register(&denoise_function)) {
377                 ast_custom_function_unregister(&agc_function);
378                 return AST_MODULE_LOAD_DECLINE;
379         }
380
381         return AST_MODULE_LOAD_SUCCESS;
382 }
383
384 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");