Merge changes from team/group/appdocsxml
[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 recieved
64                         and <literal>tx</literal> for audio transmitted to the channel. When using this
65                         function you set a target audio level. It is primarly 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 recieve.</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
146         /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
147         if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || frame->frametype != AST_FRAME_VOICE) {
148                 return 0;
149         }
150         
151         ast_channel_lock(chan);
152         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
153                 ast_channel_unlock(chan);
154                 return 0;
155         }
156         ast_channel_unlock(chan);
157
158         si = datastore->data;
159
160         sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
161
162         if (!sdi) {
163                 return 0;
164         }
165
166         if (sdi->samples != frame->samples) {
167                 if (sdi->state) {
168                         speex_preprocess_state_destroy(sdi->state);
169                 }
170
171                 if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), 8000))) {
172                         return -1;
173                 }
174                 
175                 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
176
177                 if (sdi->agc) {
178                         speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
179                 }
180
181                 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
182         }
183
184         speex_preprocess(sdi->state, frame->data.ptr, NULL);
185
186         return 0;
187 }
188
189 static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
190 {
191         struct ast_datastore *datastore = NULL;
192         struct speex_info *si = NULL;
193         struct speex_direction_info **sdi = NULL;
194         int is_new = 0;
195
196         ast_channel_lock(chan);
197         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
198                 ast_channel_unlock(chan);
199
200                 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
201                         return 0;
202                 }
203
204                 if (!(si = ast_calloc(1, sizeof(*si)))) {
205                         ast_datastore_free(datastore);
206                         return 0;
207                 }
208
209                 ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex");
210                 si->audiohook.manipulate_callback = speex_callback;
211
212                 is_new = 1;
213         } else {
214                 ast_channel_unlock(chan);
215                 si = datastore->data;
216         }
217
218         if (!strcasecmp(data, "rx")) {
219                 sdi = &si->rx;
220         } else if (!strcasecmp(data, "tx")) {
221                 sdi = &si->tx;
222         } else {
223                 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
224
225                 if (is_new) {
226                         ast_datastore_free(datastore);
227                         return -1;
228                 }
229         }
230
231         if (!*sdi) {
232                 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
233                         return 0;
234                 }
235                 /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
236                  * audio.  When it supports 16 kHz (or any other sample rates, we will
237                  * have to take that into account here. */
238                 (*sdi)->samples = -1;
239         }
240
241         if (!strcasecmp(cmd, "agc")) {
242                 if (!sscanf(value, "%f", &(*sdi)->agclevel))
243                         (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
244         
245                 if ((*sdi)->agclevel > 32768.0) {
246                         ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n", 
247                                         ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
248                         (*sdi)->agclevel = 32768.0;
249                 }
250         
251                 (*sdi)->agc = !!((*sdi)->agclevel);
252
253                 if ((*sdi)->state) {
254                         speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
255                         if ((*sdi)->agc) {
256                                 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
257                         }
258                 }
259         } else if (!strcasecmp(cmd, "denoise")) {
260                 (*sdi)->denoise = (ast_true(value) != 0);
261
262                 if ((*sdi)->state) {
263                         speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
264                 }
265         }
266
267         if (!(*sdi)->agc && !(*sdi)->denoise) {
268                 if ((*sdi)->state)
269                         speex_preprocess_state_destroy((*sdi)->state);
270
271                 ast_free(*sdi);
272                 *sdi = NULL;
273         }
274
275         if (!si->rx && !si->tx) {
276                 if (is_new) {
277                         is_new = 0;
278                 } else {
279                         ast_channel_lock(chan);
280                         ast_channel_datastore_remove(chan, datastore);
281                         ast_channel_unlock(chan);
282                         ast_audiohook_remove(chan, &si->audiohook);
283                         ast_audiohook_detach(&si->audiohook);
284                 }
285                 
286                 ast_datastore_free(datastore);
287         }
288
289         if (is_new) { 
290                 datastore->data = si;
291                 ast_channel_lock(chan);
292                 ast_channel_datastore_add(chan, datastore);
293                 ast_channel_unlock(chan);
294                 ast_audiohook_attach(chan, &si->audiohook);
295         }
296
297         return 0;
298 }
299
300 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
301 {
302         struct ast_datastore *datastore = NULL;
303         struct speex_info *si = NULL;
304         struct speex_direction_info *sdi = NULL;
305
306         if (!chan) {
307                 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
308                 return -1;
309         }
310
311         ast_channel_lock(chan);
312         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
313                 ast_channel_unlock(chan);
314                 return -1;
315         }
316         ast_channel_unlock(chan);
317
318         si = datastore->data;
319
320         if (!strcasecmp(data, "tx"))
321                 sdi = si->tx;
322         else if (!strcasecmp(data, "rx"))
323                 sdi = si->rx;
324         else {
325                 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
326                 return -1;
327         }
328
329         if (!strcasecmp(cmd, "agc"))
330                 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
331         else
332                 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
333
334         return 0;
335 }
336
337 static struct ast_custom_function agc_function = {
338         .name = "AGC",
339         .write = speex_write,
340         .read = speex_read
341 };
342
343 static struct ast_custom_function denoise_function = {
344         .name = "DENOISE",
345         .write = speex_write,
346         .read = speex_read
347 };
348
349 static int unload_module(void)
350 {
351         ast_custom_function_unregister(&agc_function);
352         ast_custom_function_unregister(&denoise_function);
353         return 0;
354 }
355
356 static int load_module(void)
357 {
358         if (ast_custom_function_register(&agc_function)) {
359                 return AST_MODULE_LOAD_DECLINE;
360         }
361
362         if (ast_custom_function_register(&denoise_function)) {
363                 ast_custom_function_unregister(&agc_function);
364                 return AST_MODULE_LOAD_DECLINE;
365         }
366
367         return AST_MODULE_LOAD_SUCCESS;
368 }
369
370 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");