Merged revisions 328247 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 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_rate(&frame->subclass.format) != si->lastrate)) {
169                 si->lastrate = ast_format_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         ast_channel_lock(chan);
206         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
207                 ast_channel_unlock(chan);
208
209                 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
210                         return 0;
211                 }
212
213                 if (!(si = ast_calloc(1, sizeof(*si)))) {
214                         ast_datastore_free(datastore);
215                         return 0;
216                 }
217
218                 ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
219                 si->audiohook.manipulate_callback = speex_callback;
220                 si->lastrate = 8000;
221                 is_new = 1;
222         } else {
223                 ast_channel_unlock(chan);
224                 si = datastore->data;
225         }
226
227         if (!strcasecmp(data, "rx")) {
228                 sdi = &si->rx;
229         } else if (!strcasecmp(data, "tx")) {
230                 sdi = &si->tx;
231         } else {
232                 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
233
234                 if (is_new) {
235                         ast_datastore_free(datastore);
236                         return -1;
237                 }
238         }
239
240         if (!*sdi) {
241                 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
242                         return 0;
243                 }
244                 /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
245                  * audio.  When it supports 16 kHz (or any other sample rates, we will
246                  * have to take that into account here. */
247                 (*sdi)->samples = -1;
248         }
249
250         if (!strcasecmp(cmd, "agc")) {
251                 if (!sscanf(value, "%30f", &(*sdi)->agclevel))
252                         (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
253         
254                 if ((*sdi)->agclevel > 32768.0) {
255                         ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n", 
256                                         ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
257                         (*sdi)->agclevel = 32768.0;
258                 }
259         
260                 (*sdi)->agc = !!((*sdi)->agclevel);
261
262                 if ((*sdi)->state) {
263                         speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
264                         if ((*sdi)->agc) {
265                                 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
266                         }
267                 }
268         } else if (!strcasecmp(cmd, "denoise")) {
269                 (*sdi)->denoise = (ast_true(value) != 0);
270
271                 if ((*sdi)->state) {
272                         speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
273                 }
274         }
275
276         if (!(*sdi)->agc && !(*sdi)->denoise) {
277                 if ((*sdi)->state)
278                         speex_preprocess_state_destroy((*sdi)->state);
279
280                 ast_free(*sdi);
281                 *sdi = NULL;
282         }
283
284         if (!si->rx && !si->tx) {
285                 if (is_new) {
286                         is_new = 0;
287                 } else {
288                         ast_channel_lock(chan);
289                         ast_channel_datastore_remove(chan, datastore);
290                         ast_channel_unlock(chan);
291                         ast_audiohook_remove(chan, &si->audiohook);
292                         ast_audiohook_detach(&si->audiohook);
293                 }
294                 
295                 ast_datastore_free(datastore);
296         }
297
298         if (is_new) { 
299                 datastore->data = si;
300                 ast_channel_lock(chan);
301                 ast_channel_datastore_add(chan, datastore);
302                 ast_channel_unlock(chan);
303                 ast_audiohook_attach(chan, &si->audiohook);
304         }
305
306         return 0;
307 }
308
309 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
310 {
311         struct ast_datastore *datastore = NULL;
312         struct speex_info *si = NULL;
313         struct speex_direction_info *sdi = NULL;
314
315         if (!chan) {
316                 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
317                 return -1;
318         }
319
320         ast_channel_lock(chan);
321         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
322                 ast_channel_unlock(chan);
323                 return -1;
324         }
325         ast_channel_unlock(chan);
326
327         si = datastore->data;
328
329         if (!strcasecmp(data, "tx"))
330                 sdi = si->tx;
331         else if (!strcasecmp(data, "rx"))
332                 sdi = si->rx;
333         else {
334                 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
335                 return -1;
336         }
337
338         if (!strcasecmp(cmd, "agc"))
339                 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
340         else
341                 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
342
343         return 0;
344 }
345
346 static struct ast_custom_function agc_function = {
347         .name = "AGC",
348         .write = speex_write,
349         .read = speex_read,
350         .read_max = 22,
351 };
352
353 static struct ast_custom_function denoise_function = {
354         .name = "DENOISE",
355         .write = speex_write,
356         .read = speex_read,
357         .read_max = 22,
358 };
359
360 static int unload_module(void)
361 {
362         ast_custom_function_unregister(&agc_function);
363         ast_custom_function_unregister(&denoise_function);
364         return 0;
365 }
366
367 static int load_module(void)
368 {
369         if (ast_custom_function_register(&agc_function)) {
370                 return AST_MODULE_LOAD_DECLINE;
371         }
372
373         if (ast_custom_function_register(&denoise_function)) {
374                 ast_custom_function_unregister(&agc_function);
375                 return AST_MODULE_LOAD_DECLINE;
376         }
377
378         return AST_MODULE_LOAD_SUCCESS;
379 }
380
381 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");