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