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