Media Project Phase2: SILK 8khz-24khz, SLINEAR 8khz-192khz, SPEEX 32khz, hd audio...
[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         int lastrate;
109         struct speex_direction_info *tx, *rx;
110 };
111
112 static void destroy_callback(void *data) 
113 {
114         struct speex_info *si = data;
115
116         ast_audiohook_destroy(&si->audiohook);
117
118         if (si->rx && si->rx->state) {
119                 speex_preprocess_state_destroy(si->rx->state);
120         }
121
122         if (si->tx && si->tx->state) {
123                 speex_preprocess_state_destroy(si->tx->state);
124         }
125
126         if (si->rx) {
127                 ast_free(si->rx);
128         }
129
130         if (si->tx) {
131                 ast_free(si->tx);
132         }
133
134         ast_free(data);
135 };
136
137 static const struct ast_datastore_info speex_datastore = {
138         .type = "speex",
139         .destroy = destroy_callback
140 };
141
142 static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
143 {
144         struct ast_datastore *datastore = NULL;
145         struct speex_direction_info *sdi = NULL;
146         struct speex_info *si = NULL;
147         char source[80];
148
149         /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
150         if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || frame->frametype != AST_FRAME_VOICE) {
151                 return -1;
152         }
153
154         /* We are called with chan already locked */
155         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
156                 return -1;
157         }
158
159         si = datastore->data;
160
161         sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
162
163         if (!sdi) {
164                 return -1;
165         }
166
167         if ((sdi->samples != frame->samples) || (ast_format_rate(&frame->subclass.format) != si->lastrate)) {
168                 si->lastrate = ast_format_rate(&frame->subclass.format);
169                 if (sdi->state) {
170                         speex_preprocess_state_destroy(sdi->state);
171                 }
172
173                 if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), si->lastrate))) {
174                         return -1;
175                 }
176
177                 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
178
179                 if (sdi->agc) {
180                         speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
181                 }
182
183                 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
184         }
185
186         speex_preprocess(sdi->state, frame->data.ptr, NULL);
187         snprintf(source, sizeof(source), "%s/speex", frame->src);
188         if (frame->mallocd & AST_MALLOCD_SRC) {
189                 ast_free((char *) frame->src);
190         }
191         frame->src = ast_strdup(source);
192         frame->mallocd |= AST_MALLOCD_SRC;
193
194         return 0;
195 }
196
197 static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
198 {
199         struct ast_datastore *datastore = NULL;
200         struct speex_info *si = NULL;
201         struct speex_direction_info **sdi = NULL;
202         int is_new = 0;
203
204         ast_channel_lock(chan);
205         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
206                 ast_channel_unlock(chan);
207
208                 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
209                         return 0;
210                 }
211
212                 if (!(si = ast_calloc(1, sizeof(*si)))) {
213                         ast_datastore_free(datastore);
214                         return 0;
215                 }
216
217                 ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
218                 si->audiohook.manipulate_callback = speex_callback;
219                 si->lastrate = 8000;
220                 is_new = 1;
221         } else {
222                 ast_channel_unlock(chan);
223                 si = datastore->data;
224         }
225
226         if (!strcasecmp(data, "rx")) {
227                 sdi = &si->rx;
228         } else if (!strcasecmp(data, "tx")) {
229                 sdi = &si->tx;
230         } else {
231                 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
232
233                 if (is_new) {
234                         ast_datastore_free(datastore);
235                         return -1;
236                 }
237         }
238
239         if (!*sdi) {
240                 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
241                         return 0;
242                 }
243                 /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
244                  * audio.  When it supports 16 kHz (or any other sample rates, we will
245                  * have to take that into account here. */
246                 (*sdi)->samples = -1;
247         }
248
249         if (!strcasecmp(cmd, "agc")) {
250                 if (!sscanf(value, "%30f", &(*sdi)->agclevel))
251                         (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
252         
253                 if ((*sdi)->agclevel > 32768.0) {
254                         ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n", 
255                                         ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
256                         (*sdi)->agclevel = 32768.0;
257                 }
258         
259                 (*sdi)->agc = !!((*sdi)->agclevel);
260
261                 if ((*sdi)->state) {
262                         speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
263                         if ((*sdi)->agc) {
264                                 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
265                         }
266                 }
267         } else if (!strcasecmp(cmd, "denoise")) {
268                 (*sdi)->denoise = (ast_true(value) != 0);
269
270                 if ((*sdi)->state) {
271                         speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
272                 }
273         }
274
275         if (!(*sdi)->agc && !(*sdi)->denoise) {
276                 if ((*sdi)->state)
277                         speex_preprocess_state_destroy((*sdi)->state);
278
279                 ast_free(*sdi);
280                 *sdi = NULL;
281         }
282
283         if (!si->rx && !si->tx) {
284                 if (is_new) {
285                         is_new = 0;
286                 } else {
287                         ast_channel_lock(chan);
288                         ast_channel_datastore_remove(chan, datastore);
289                         ast_channel_unlock(chan);
290                         ast_audiohook_remove(chan, &si->audiohook);
291                         ast_audiohook_detach(&si->audiohook);
292                 }
293                 
294                 ast_datastore_free(datastore);
295         }
296
297         if (is_new) { 
298                 datastore->data = si;
299                 ast_channel_lock(chan);
300                 ast_channel_datastore_add(chan, datastore);
301                 ast_channel_unlock(chan);
302                 ast_audiohook_attach(chan, &si->audiohook);
303         }
304
305         return 0;
306 }
307
308 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
309 {
310         struct ast_datastore *datastore = NULL;
311         struct speex_info *si = NULL;
312         struct speex_direction_info *sdi = NULL;
313
314         if (!chan) {
315                 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
316                 return -1;
317         }
318
319         ast_channel_lock(chan);
320         if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
321                 ast_channel_unlock(chan);
322                 return -1;
323         }
324         ast_channel_unlock(chan);
325
326         si = datastore->data;
327
328         if (!strcasecmp(data, "tx"))
329                 sdi = si->tx;
330         else if (!strcasecmp(data, "rx"))
331                 sdi = si->rx;
332         else {
333                 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
334                 return -1;
335         }
336
337         if (!strcasecmp(cmd, "agc"))
338                 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
339         else
340                 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
341
342         return 0;
343 }
344
345 static struct ast_custom_function agc_function = {
346         .name = "AGC",
347         .write = speex_write,
348         .read = speex_read,
349         .read_max = 22,
350 };
351
352 static struct ast_custom_function denoise_function = {
353         .name = "DENOISE",
354         .write = speex_write,
355         .read = speex_read,
356         .read_max = 22,
357 };
358
359 static int unload_module(void)
360 {
361         ast_custom_function_unregister(&agc_function);
362         ast_custom_function_unregister(&denoise_function);
363         return 0;
364 }
365
366 static int load_module(void)
367 {
368         if (ast_custom_function_register(&agc_function)) {
369                 return AST_MODULE_LOAD_DECLINE;
370         }
371
372         if (ast_custom_function_register(&denoise_function)) {
373                 ast_custom_function_unregister(&agc_function);
374                 return AST_MODULE_LOAD_DECLINE;
375         }
376
377         return AST_MODULE_LOAD_SUCCESS;
378 }
379
380 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");