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