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