Asterisk - The Open Source Telephony Project GIT-master-2de1a68
func_speex.c
Go to the documentation of this file.
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 <example title="Apply automatic gain control">
70 exten => 1,1,Set(AGC(rx)=8000)
71 exten => 1,2,Set(AGC(tx)=off)
72 </example>
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 <example title="Apply noise reduction">
92 exten => 1,1,Set(DENOISE(rx)=on)
93 exten => 1,2,Set(DENOISE(tx)=off)
94 </example>
95 </description>
96 </function>
97 ***/
98
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
111};
112
113static void destroy_callback(void *data)
114{
115 struct speex_info *si = data;
116
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
138static const struct ast_datastore_info speex_datastore = {
139 .type = "speex",
140 .destroy = destroy_callback
141};
142
143static 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 */
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_get_sample_rate(frame->subclass.format) != si->lastrate)) {
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
198static 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 (!chan) {
206 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
207 return -1;
208 }
209
210 if (strcasecmp(data, "rx") && strcasecmp(data, "tx")) {
211 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
212 return -1;
213 }
214
215 ast_channel_lock(chan);
216 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
217 ast_channel_unlock(chan);
218
219 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
220 return 0;
221 }
222
223 if (!(si = ast_calloc(1, sizeof(*si)))) {
224 ast_datastore_free(datastore);
225 return 0;
226 }
227
230 si->lastrate = 8000;
231 is_new = 1;
232 } else {
233 ast_channel_unlock(chan);
234 si = datastore->data;
235 }
236
237 if (!strcasecmp(data, "rx")) {
238 sdi = &si->rx;
239 } else {
240 sdi = &si->tx;
241 }
242
243 if (!*sdi) {
244 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
245 return 0;
246 }
247 /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
248 * audio. When it supports 16 kHz (or any other sample rates, we will
249 * have to take that into account here. */
250 (*sdi)->samples = -1;
251 }
252
253 if (!strcasecmp(cmd, "agc")) {
254 if (!sscanf(value, "%30f", &(*sdi)->agclevel))
255 (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
256
257 if ((*sdi)->agclevel > 32768.0) {
258 ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n",
259 ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
260 (*sdi)->agclevel = 32768.0;
261 }
262
263 (*sdi)->agc = !!((*sdi)->agclevel);
264
265 if ((*sdi)->state) {
266 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
267 if ((*sdi)->agc) {
268 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
269 }
270 }
271 } else if (!strcasecmp(cmd, "denoise")) {
272 (*sdi)->denoise = (ast_true(value) != 0);
273
274 if ((*sdi)->state) {
275 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
276 }
277 }
278
279 if (!(*sdi)->agc && !(*sdi)->denoise) {
280 if ((*sdi)->state)
281 speex_preprocess_state_destroy((*sdi)->state);
282
283 ast_free(*sdi);
284 *sdi = NULL;
285 }
286
287 if (!si->rx && !si->tx) {
288 if (is_new) {
289 is_new = 0;
290 } else {
291 ast_channel_lock(chan);
292 ast_channel_datastore_remove(chan, datastore);
293 ast_channel_unlock(chan);
296 }
297
298 ast_datastore_free(datastore);
299 }
300
301 if (is_new) {
302 datastore->data = si;
303 ast_channel_lock(chan);
304 ast_channel_datastore_add(chan, datastore);
305 ast_channel_unlock(chan);
307 }
308
309 return 0;
310}
311
312static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
313{
314 struct ast_datastore *datastore = NULL;
315 struct speex_info *si = NULL;
316 struct speex_direction_info *sdi = NULL;
317
318 if (!chan) {
319 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
320 return -1;
321 }
322
323 ast_channel_lock(chan);
324 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
325 ast_channel_unlock(chan);
326 return -1;
327 }
328 ast_channel_unlock(chan);
329
330 si = datastore->data;
331
332 if (!strcasecmp(data, "tx"))
333 sdi = si->tx;
334 else if (!strcasecmp(data, "rx"))
335 sdi = si->rx;
336 else {
337 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
338 return -1;
339 }
340
341 if (!strcasecmp(cmd, "agc"))
342 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
343 else
344 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
345
346 return 0;
347}
348
350 .name = "AGC",
351 .write = speex_write,
352 .read = speex_read,
353 .read_max = 22,
354};
355
357 .name = "DENOISE",
358 .write = speex_write,
359 .read = speex_read,
360 .read_max = 22,
361};
362
363static int unload_module(void)
364{
367 return 0;
368}
369
370static int load_module(void)
371{
374 }
375
379 }
380
382}
383
384AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");
if(!yyg->yy_init)
Definition: ast_expr2f.c:854
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
Audiohooks Architecture.
@ AST_AUDIOHOOK_MANIPULATE_ALL_RATES
Definition: audiohook.h:75
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags flags)
Initialize an audiohook structure.
Definition: audiohook.c:100
int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audiohook)
Remove an audiohook from a specified channel.
Definition: audiohook.c:721
ast_audiohook_direction
Definition: audiohook.h:48
@ AST_AUDIOHOOK_DIRECTION_READ
Definition: audiohook.h:49
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition: audiohook.c:550
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition: audiohook.c:484
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition: audiohook.c:124
@ AST_AUDIOHOOK_TYPE_MANIPULATE
Definition: audiohook.h:38
@ AST_AUDIOHOOK_STATUS_DONE
Definition: audiohook.h:45
General Asterisk PBX channel definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
#define ast_channel_lock(chan)
Definition: channel.h:2922
#define ast_channel_unlock(chan)
Definition: channel.h:2923
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
direction
static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_speex.c:312
static struct ast_custom_function agc_function
Definition: func_speex.c:349
#define DEFAULT_AGC_LEVEL
Definition: func_speex.c:49
static struct ast_custom_function denoise_function
Definition: func_speex.c:356
static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Definition: func_speex.c:198
static void destroy_callback(void *data)
Definition: func_speex.c:113
static int load_module(void)
Definition: func_speex.c:370
static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
Definition: func_speex.c:143
static int unload_module(void)
Definition: func_speex.c:363
static const struct ast_datastore_info speex_datastore
Definition: func_speex.c:138
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define AST_MALLOCD_SRC
@ AST_FRAME_VOICE
#define LOG_ERROR
#define LOG_WARNING
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:567
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Core PBX routines and definitions.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
#define NULL
Definition: resample.c:96
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
ast_audiohook_manipulate_callback manipulate_callback
Definition: audiohook.h:118
enum ast_audiohook_status status
Definition: audiohook.h:108
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
struct ast_format * format
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
union ast_frame::@226 data
enum ast_frame_type frametype
const char * src
SpeexPreprocessState * state
Definition: func_speex.c:100
struct speex_direction_info * rx
Definition: func_speex.c:110
struct ast_audiohook audiohook
Definition: func_speex.c:108
int lastrate
Definition: func_speex.c:109
struct speex_direction_info * tx
Definition: func_speex.c:110
int value
Definition: syslog.c:37
Utility functions.